From 4e58b57e959343ac247717ab9a94286bd0ab91bb Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 25 Apr 2022 22:44:31 +1200 Subject: [PATCH] Scroll at a consistent speed. Fixes #3607 --- src/controller_base.cpp | 58 +++++++++++++++++++++++++++++++++-------- src/controller_base.hpp | 6 +++++ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/controller_base.cpp b/src/controller_base.cpp index 57e8ed00053..709da882017 100644 --- a/src/controller_base.cpp +++ b/src/controller_base.cpp @@ -43,6 +43,9 @@ controller_base::controller_base() , scroll_down_(false) , scroll_left_(false) , scroll_right_(false) + , last_scroll_tick_(0) + , scroll_carry_x_(0.0) + , scroll_carry_y_(0.0) , key_release_listener_(*this) , last_mouse_is_touch_(false) , long_touch_timer_(0) @@ -273,7 +276,7 @@ bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags) || preferences::get("scroll_when_mouse_outside", true); int scroll_speed = preferences::scroll_speed(); - int dx = 0, dy = 0; + double dx = 0.0, dy = 0.0; int scroll_threshold = preferences::mouse_scroll_enabled() ? preferences::mouse_scroll_threshold() @@ -285,28 +288,41 @@ bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags) } } + // Scale scroll distance according to time passed + uint32_t tick_now = SDL_GetTicks(); + // If we weren't previously scrolling, start small. + int dt = 1; + if (scrolling_) { + dt = tick_now - last_scroll_tick_; + } + // scroll_speed is in percent. Ticks are in milliseconds. + // Let's assume the maximum speed (100) moves 50 hexes per second, + // i.e. 3600 pixels per 1000 ticks. + double scroll_amount = double(dt) * 0.036 * double(scroll_speed); + last_scroll_tick_ = tick_now; + // Apply keyboard scrolling - dy -= scroll_up_ * scroll_speed; - dy += scroll_down_ * scroll_speed; - dx -= scroll_left_ * scroll_speed; - dx += scroll_right_ * scroll_speed; + dy -= scroll_up_ * scroll_amount; + dy += scroll_down_ * scroll_amount; + dx -= scroll_left_ * scroll_amount; + dx += scroll_right_ * scroll_amount; // Scroll if mouse is placed near the edge of the screen if(mouse_in_window) { if(mousey < scroll_threshold) { - dy -= scroll_speed; + dy -= scroll_amount; } if(mousey > get_display().video().get_height() - scroll_threshold) { - dy += scroll_speed; + dy += scroll_amount; } if(mousex < scroll_threshold) { - dx -= scroll_speed; + dx -= scroll_amount; } if(mousex > get_display().video().get_width() - scroll_threshold) { - dx += scroll_speed; + dx += scroll_amount; } } @@ -322,7 +338,7 @@ bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags) if(sdl::point_in_rect(mousex, mousey, rect) && mh_base.scroll_started()) { // Scroll speed is proportional from the distance from the first // middle click and scrolling speed preference. - const double speed = 0.04 * std::sqrt(static_cast(scroll_speed)); + const double speed = 0.01 * scroll_amount; const double snap_dist = 16; // Snap to horizontal/vertical scrolling const double x_diff = (mousex - original_loc.x); const double y_diff = (mousey - original_loc.y); @@ -340,7 +356,27 @@ bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags) } } - return get_display().scroll(dx, dy); + // If nothing is scrolling, just return. + if (!dx && !dy) { + return false; + } + + // If we are continuing a scroll, carry over any subpixel movement. + if (scrolling_) { + dx += scroll_carry_x_; + dy += scroll_carry_y_; + } + int dx_int = int(dx); + int dy_int = int(dy); + scroll_carry_x_ = dx - double(dx_int); + scroll_carry_y_ = dy - double(dy_int); + + // Scroll the display + get_display().scroll(dx_int, dy_int); + + // Even if the integer parts are both zero, we are still scrolling. + // The subpixel amounts will add up. + return true; } void controller_base::play_slice(bool is_delay_enabled) diff --git a/src/controller_base.hpp b/src/controller_base.hpp index a9d83ebab13..22f88cf8738 100644 --- a/src/controller_base.hpp +++ b/src/controller_base.hpp @@ -189,6 +189,12 @@ protected: bool scroll_down_; bool scroll_left_; bool scroll_right_; + /* When the last scroll tick was processed */ + uint32_t last_scroll_tick_; + /* Sub-pixel movement left over from a previous scroll tick. + * This is added to the next scroll tick, if scrolling continues. */ + double scroll_carry_x_; + double scroll_carry_y_; private: /* A separate class for listening key-up events.