Update the UI at screen refresh rate (#20214)

* Update the UI at screen refresh rate

* Decouple input from ticks, fix scroll at high frame rates

* Fix holding down mouse button on buttons causing too many events

* Subtract the initial delay to keep the same behavior as before

* Guard against the rare case where the value might be 0

* Fix right click not working correctly

* Fix odd behavior when using right click to scroll lists

* Make touch work again, fix mouse panning in fullscreen (borderless)

* Update changelog.txt
This commit is contained in:
Matthias Moninger 2023-05-17 22:19:44 +03:00 committed by GitHub
parent 0b18bc0923
commit 6bd5f75330
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 24 deletions

View File

@ -3,6 +3,7 @@
- Feature: [OpenMusic#41] Official Title Theme by Allister Brimble.
- Improved: [#20200] Allow audio files to play to up to 44100hz sample rate (from 22050hz).
- Change: [#20110] Fix a few RCT1 build height parity discrepancies.
- Fix: [#6152] Camera and UI are no longer locked at 40hz providing a smoother experience.
- Fix: [#19823] Parkobj, disallow overriding objects of different object types.
- Fix: [#20111] All coaster types can access the new diagonal slope pieces.
- Fix: [#20155] Fairground organ style 2 shows up as regular song, rather than for the merry-go-round.

View File

@ -32,6 +32,7 @@
#include <openrct2/world/Banner.h>
#include <openrct2/world/Map.h>
#include <openrct2/world/Scenery.h>
#include <optional>
struct RCTMouseData
{
@ -44,7 +45,7 @@ static RCTMouseData _mouseInputQueue[64];
static uint8_t _mouseInputQueueReadIndex = 0;
static uint8_t _mouseInputQueueWriteIndex = 0;
static uint32_t _ticksSinceDragStart;
static std::optional<uint32_t> _ticksSinceDragStart;
static WidgetRef _dragWidget;
static uint8_t _dragScrollIndex;
static int32_t _originalWindowWidth;
@ -59,7 +60,7 @@ uint16_t gTooltipTimeout;
WidgetRef gTooltipWidget;
ScreenCoordsXY gTooltipCursor;
static int16_t _clickRepeatTicks;
static std::optional<uint32_t> _clickRepeatTicks;
static MouseState GameGetNextInput(ScreenCoordsXY& screenCoords);
static void InputWidgetOver(const ScreenCoordsXY& screenCoords, WindowBase* w, WidgetIndex widgetIndex);
@ -178,7 +179,7 @@ static void InputScrollDragBegin(const ScreenCoordsXY& screenCoords, WindowBase*
_dragWidget.window_classification = w->classification;
_dragWidget.window_number = w->number;
_dragWidget.widget_index = widgetIndex;
_ticksSinceDragStart = 0;
_ticksSinceDragStart = gCurrentRealTimeTicks;
_dragScrollIndex = WindowGetScrollDataIndex(*w, widgetIndex);
ContextHideCursor();
@ -197,6 +198,8 @@ static void InputScrollDragContinue(const ScreenCoordsXY& screenCoords, WindowBa
auto& scroll = w->scrolls[scrollIndex];
ScreenCoordsXY differentialCoords = screenCoords - gInputDragLast;
if (differentialCoords.x == 0 && differentialCoords.y == 0)
return;
if (scroll.flags & HSCROLLBAR_VISIBLE)
{
@ -242,10 +245,9 @@ static void InputScrollRight(const ScreenCoordsXY& screenCoords, MouseState stat
switch (state)
{
case MouseState::Released:
_ticksSinceDragStart += gCurrentDeltaTime;
if (screenCoords.x != 0 || screenCoords.y != 0)
{
_ticksSinceDragStart = 1000;
_ticksSinceDragStart = std::nullopt;
InputScrollDragContinue(screenCoords, w);
}
break;
@ -348,7 +350,7 @@ static void GameHandleInputMouse(const ScreenCoordsXY& screenCoords, MouseState
else if (state == MouseState::RightRelease)
{
InputViewportDragEnd();
if (_ticksSinceDragStart < 500)
if (_ticksSinceDragStart.has_value() && gCurrentRealTimeTicks - _ticksSinceDragStart.value() < 500)
{
// If the user pressed the right mouse button for less than 500 ticks, interpret as right click
ViewportInteractionRightClick(screenCoords);
@ -525,7 +527,7 @@ static void InputViewportDragBegin(WindowBase& w)
_inputState = InputState::ViewportRight;
_dragWidget.window_classification = w.classification;
_dragWidget.window_number = w.number;
_ticksSinceDragStart = 0;
_ticksSinceDragStart = gCurrentRealTimeTicks;
auto cursorPosition = ContextGetCursorPosition();
gInputDragLast = cursorPosition;
if (!gConfigGeneral.InvertViewportDrag)
@ -543,9 +545,11 @@ static void InputViewportDragContinue()
Viewport* viewport;
auto newDragCoords = ContextGetCursorPosition();
const CursorState* cursorState = ContextGetCursorState();
auto differentialCoords = newDragCoords - gInputDragLast;
if (differentialCoords.x == 0 && differentialCoords.y == 0)
return;
w = WindowFindByNumber(_dragWidget.window_classification, _dragWidget.window_number);
// #3294: Window can be closed during a drag session, so just finish
@ -557,7 +561,6 @@ static void InputViewportDragContinue()
}
viewport = w->viewport;
_ticksSinceDragStart += gCurrentDeltaTime;
if (viewport == nullptr)
{
ContextShowCursor();
@ -571,7 +574,7 @@ static void InputViewportDragContinue()
// If the drag time is less than 500 the "drag" is usually interpreted as a right click.
// As the user moved the mouse, don't interpret it as right click in any case.
_ticksSinceDragStart = 1000;
_ticksSinceDragStart = std::nullopt;
differentialCoords.x = (viewport->zoom + 1).ApplyTo(differentialCoords.x);
differentialCoords.y = (viewport->zoom + 1).ApplyTo(differentialCoords.y);
@ -586,6 +589,7 @@ static void InputViewportDragContinue()
}
}
const CursorState* cursorState = ContextGetCursorState();
if (cursorState->touch || gConfigGeneral.InvertViewportDrag)
{
gInputDragLast = newDragCoords;
@ -1086,7 +1090,7 @@ static void InputWidgetLeft(const ScreenCoordsXY& screenCoords, WindowBase* w, W
gPressedWidget.widget_index = widgetIndex;
_inputFlags |= INPUT_FLAG_WIDGET_PRESSED;
_inputState = InputState::WidgetPressed;
_clickRepeatTicks = 1;
_clickRepeatTicks = gCurrentRealTimeTicks;
WidgetInvalidateByNumber(windowClass, windowNumber, widgetIndex);
WindowEventMouseDownCall(w, widgetIndex);
@ -1304,17 +1308,28 @@ void InputStateWidgetPressed(
if (WidgetIsDisabled(*w, widgetIndex))
break;
if (_clickRepeatTicks != 0)
// If this variable is non-zero then its the last tick the mouse down event was fired.
if (_clickRepeatTicks.has_value())
{
_clickRepeatTicks++;
// The initial amount of time in ticks to wait until the first click repeat.
constexpr auto ticksUntilRepeats = 16U;
// Handle click repeat
if (_clickRepeatTicks >= 16 && (_clickRepeatTicks & 3) == 0)
// The amount of ticks between each click repeat.
constexpr auto eventDelayInTicks = 3U;
// The amount of ticks since the last click repeat.
const auto clickRepeatsDelta = gCurrentRealTimeTicks - _clickRepeatTicks.value();
// Handle click repeat, only start this when at least 16 ticks elapsed.
if (clickRepeatsDelta >= ticksUntilRepeats && (clickRepeatsDelta & eventDelayInTicks) == 0)
{
if (WidgetIsHoldable(*w, widgetIndex))
{
WindowEventMouseDownCall(w, widgetIndex);
}
// Subtract initial delay from here on we want the event each third tick.
_clickRepeatTicks = gCurrentRealTimeTicks - ticksUntilRepeats;
}
}
@ -1439,7 +1454,7 @@ void InputStateWidgetPressed(
return;
}
_clickRepeatTicks = 0;
_clickRepeatTicks = std::nullopt;
if (_inputState != InputState::DropdownActive)
{
// Hold down widget and drag outside of area??

View File

@ -1112,12 +1112,12 @@ namespace OpenRCT2
{
Tick();
// Always run this at a fixed rate, Update can cause multiple ticks if the game is speed up.
WindowUpdateAll();
_ticksAccumulator -= GAME_UPDATE_TIME_MS;
}
ContextHandleInput();
WindowUpdateAll();
if (ShouldDraw())
{
Draw();
@ -1141,9 +1141,6 @@ namespace OpenRCT2
Tick();
// Always run this at a fixed rate, Update can cause multiple ticks if the game is speed up.
WindowUpdateAll();
_ticksAccumulator -= GAME_UPDATE_TIME_MS;
// Get the next position of each sprite
@ -1151,6 +1148,9 @@ namespace OpenRCT2
tweener.PostTick();
}
ContextHandleInput();
WindowUpdateAll();
if (shouldDraw)
{
const float alpha = std::min(_ticksAccumulator / GAME_UPDATE_TIME_MS, 1.0f);

View File

@ -230,8 +230,6 @@ void GameState::Tick()
gWindowMapFlashingFlags &= ~MapFlashingFlags::StaffListOpen;
ContextUpdateMapTooltip();
ContextHandleInput();
}
// Always perform autosave check, even when paused