Merge remote-tracking branch 'origin/master' into HEAD

This commit is contained in:
Sergii Pylypenko
2019-04-23 18:16:24 +03:00
179 changed files with 6418 additions and 1144 deletions

View File

@@ -40,6 +40,7 @@
#include "network/network_func.h"
#include "guitimer_func.h"
#include "news_func.h"
#include "build_confirmation_func.h"
#include "safeguards.h"
@@ -51,8 +52,11 @@ enum ViewportAutoscrolling {
VA_EVERY_VIEWPORT, //!< Scroll all viewports at their edges.
};
static bool _dragging_window; ///< A window is being dragged or resized.
static Point _drag_delta; ///< delta between mouse cursor and upper left corner of dragged window
static Window *_mouseover_last_w = NULL; ///< Window of the last OnMouseOver event.
static Point _left_button_down_pos; ///< Position of left mouse button down event, to handle the difference between click and drag
static bool _dragging_widget; ///< A widget inside the window is being dragged, prevent the window itself from being dragged
static Window *_last_scroll_window = NULL; ///< Window of the last scroll event.
/** List of windows opened at the screen sorted from the front. */
@@ -78,6 +82,7 @@ byte _scroller_click_timeout = 0;
bool _scrolling_viewport; ///< A viewport is being scrolled with the mouse.
bool _mouse_hovering; ///< The mouse is hovering over the same point.
static bool _left_button_dragged;
SpecialMouseMode _special_mouse_mode; ///< Mode of the mouse.
@@ -627,13 +632,14 @@ static void StartWindowDrag(Window *w);
static void StartWindowSizing(Window *w, bool to_left);
/**
* Dispatch left mouse-button (possibly double) click in window.
* Mouse left button down+up events trigger OnClick event for a window.
* @param w Window to dispatch event in
* @param x X coordinate of the click
* @param y Y coordinate of the click
* @param click_count Number of fast consecutive clicks at same position
* @param mouse_down Event generated on mouse button press
*/
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count, bool mouse_down)
{
NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
WidgetType widget_type = (nw != NULL) ? nw->type : WWT_EMPTY;
@@ -670,18 +676,35 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
focused_widget_changed |= w->SetFocusedWidget(widget_index);
}
Point pt = { x, y };
if (!_settings_client.gui.windows_titlebars && mouse_down) {
// TODO: this should be handled inside window or widget methods, not here
if (widget_type == NWID_VSCROLLBAR || widget_type == NWID_HSCROLLBAR) {
ScrollbarClickHandler(w, nw, x, y);
_dragging_widget = true;
}
if (widget_type == WWT_RESIZEBOX) {
StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
nw->SetDirty(w);
}
if (w->window_class == WC_SMALLMAP && widget_index == WID_SM_MAP) {
w->OnClick(pt, widget_index, click_count);
_dragging_widget = true;
}
return;
}
/* Close any child drop down menus. If the button pressed was the drop down
* list's own button, then we should not process the click any further. */
if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
Point pt = { x, y };
switch (widget_type) {
case NWID_VSCROLLBAR:
case NWID_HSCROLLBAR:
ScrollbarClickHandler(w, nw, x, y);
if (mouse_down) ScrollbarClickHandler(w, nw, x, y);
break;
case WWT_EDITBOX: {
@@ -759,6 +782,33 @@ static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
w->OnClick(pt, widget_index, click_count);
}
static int _left_button_click_count = 0;
/**
* Dispatch left mouse-button (possibly double) press in window.
* @param w Window to dispatch event in
* @param x X coordinate of the click
* @param y Y coordinate of the click
* @param click_count Number of fast consecutive clicks at same position
*/
static void DispatchLeftButtonDownEvent(Window *w, int x, int y, int click_count)
{
_left_button_click_count = click_count;
DispatchLeftClickEvent(w, x, y, click_count, true);
}
/**
* Dispatch left mouse-button (possibly double) release in window.
* @param w Window to dispatch event in
* @param x X coordinate of the click
* @param y Y coordinate of the click
*/
static void DispatchLeftButtonUpEvent(Window *w, int x, int y)
{
_dragging_widget = false;
if (_settings_client.gui.windows_titlebars || _dragging_window) return;
DispatchLeftClickEvent(w, x, y, _left_button_click_count, false);
}
/**
* Dispatch right mouse-button click in window.
* @param w Window to dispatch event in
@@ -1259,6 +1309,7 @@ static inline bool IsVitalWindow(const Window *w)
{
switch (w->window_class) {
case WC_MAIN_TOOLBAR:
case WC_MAIN_TOOLBAR_RIGHT:
case WC_STATUS_BAR:
case WC_NEWS_WINDOW:
case WC_SEND_NETWORK_MSG:
@@ -1301,7 +1352,7 @@ static uint GetWindowZPriority(WindowClass wc)
FALLTHROUGH;
case WC_MAIN_TOOLBAR:
case WC_STATUS_BAR:
case WC_MAIN_TOOLBAR_RIGHT:
++z_priority;
FALLTHROUGH;
@@ -1338,11 +1389,14 @@ static uint GetWindowZPriority(WindowClass wc)
++z_priority;
FALLTHROUGH;
case WC_NEWS_WINDOW:
default:
++z_priority;
FALLTHROUGH;
default:
case WC_STATUS_BAR:
++z_priority;
case WC_NEWS_WINDOW:
++z_priority;
FALLTHROUGH;
@@ -1453,6 +1507,14 @@ void Window::InitializeData(WindowNumber window_number)
this->nested_focus = NULL;
this->window_number = window_number;
if (this->window_class != WC_BUILD_CONFIRMATION &&
this->window_class != WC_TOOLTIPS &&
this->window_class != WC_NEWS_WINDOW &&
this->window_class != WC_BUILD_BRIDGE &&
this->window_class != WC_SELECT_STATION) {
HideBuildConfirmationWindow();
}
this->OnInit();
/* Initialize nested widget tree. */
if (this->nested_array == NULL) {
@@ -1507,6 +1569,7 @@ void Window::FindWindowPlacementAndResize(int def_width, int def_height)
{
def_width = max(def_width, this->width); // Don't allow default size to be smaller than smallest size
def_height = max(def_height, this->height);
bool vertical_toolbar = _settings_client.gui.vertical_toolbar && _game_mode != GM_EDITOR;
/* Try to make windows smaller when our window is too small.
* w->(width|height) is normally the same as min_(width|height),
* but this way the GUIs can be made a little more dynamic;
@@ -1518,10 +1581,13 @@ void Window::FindWindowPlacementAndResize(int def_width, int def_height)
const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
if (wt != NULL) free_height -= wt->height;
wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
if (wt != NULL) free_height -= wt->height;
if (wt != NULL && !vertical_toolbar) free_height -= wt->height;
int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
if (wt && vertical_toolbar && enlarge_x > _screen.width - wt->width * 2) {
enlarge_x = _screen.width - wt->width * 2;
}
/* X and Y has to go by step.. calculate it.
* The cast to int is necessary else x/y are implicitly casted to
@@ -1542,8 +1608,13 @@ void Window::FindWindowPlacementAndResize(int def_width, int def_height)
if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
nx = max(nx, 0);
if (!vertical_toolbar) {
ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
nx = max(nx, 0);
} else {
nx = max(nx, (wt == NULL || this == wt || this == FindWindowById(WC_MAIN_TOOLBAR_RIGHT, 0) || this->left == 0) ? 0 : wt->width);
ny = max(ny, 0);
}
if (this->viewport != NULL) {
this->viewport->left += nx - this->left;
@@ -1572,7 +1643,13 @@ static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolb
int right = width + left;
int bottom = height + top;
if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
bool vertical_toolbar = _settings_client.gui.vertical_toolbar && _game_mode != GM_EDITOR;
if (!vertical_toolbar || !main_toolbar) {
if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false;
} else {
if (left < main_toolbar->width || top < 0 || right > _screen.width - main_toolbar->width || bottom > _screen.height) return false;
}
/* Make sure it is not obscured by any window. */
const Window *w;
@@ -1647,13 +1724,18 @@ static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolb
static Point GetAutoPlacePosition(int width, int height)
{
Point pt;
bool vertical_toolbar = _settings_client.gui.vertical_toolbar && _game_mode != GM_EDITOR;
bool rtl = _current_text_dir == TD_RTL;
/* First attempt, try top-left of the screen */
const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
const int toolbar_y = main_toolbar != NULL ? main_toolbar->height : 0;
if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
const int toolbar_y = !vertical_toolbar && main_toolbar != NULL ? main_toolbar->height : 0;
if (vertical_toolbar) {
if (IsGoodAutoPlace1(main_toolbar != NULL ? main_toolbar->width : 0, 0, width, height, toolbar_y, pt)) return pt;
} else {
if (IsGoodAutoPlace1(rtl ? _screen.width - width : 0, toolbar_y, width, height, toolbar_y, pt)) return pt;
}
/* Second attempt, try around all existing windows.
* The new window must be entirely on-screen, and not overlap with an existing window.
@@ -1690,6 +1772,10 @@ static Point GetAutoPlacePosition(int width, int height)
* of the closebox
*/
int left = rtl ? _screen.width - width : 0, top = toolbar_y;
if (vertical_toolbar) {
left = main_toolbar != NULL ? (rtl ? _screen.width - width - main_toolbar->width : main_toolbar->width) : 0;
top = 0;
}
int offset_x = rtl ? -(int)NWidgetLeaf::closebox_dimension.width : (int)NWidgetLeaf::closebox_dimension.width;
int offset_y = max<int>(NWidgetLeaf::closebox_dimension.height, FONT_HEIGHT_NORMAL + WD_CAPTIONTEXT_TOP + WD_CAPTIONTEXT_BOTTOM);
@@ -1717,7 +1803,15 @@ Point GetToolbarAlignedWindowPosition(int window_width)
{
const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
assert(w != NULL);
Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
Point pt;
if (_settings_client.gui.vertical_toolbar && _game_mode != GM_EDITOR) {
// Retermine if the window was opened from the left or the right toolbar
pt.x = (_last_clicked_toolbar_idx == 0) ? w->left + w->width : _screen.width - w->width - window_width;
pt.y = w->top;
} else {
pt.x = _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width;
pt.y = w->top + w->height;
}
return pt;
}
@@ -1746,11 +1840,20 @@ static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int
int16 default_width = max(desc->GetDefaultWidth(), sm_width);
int16 default_height = max(desc->GetDefaultHeight(), sm_height);
if (desc->parent_cls != WC_NONE && (w = FindWindowById(desc->parent_cls, window_number)) != NULL) {
if (desc->parent_cls != WC_NONE && (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
bool rtl = _current_text_dir == TD_RTL;
if (desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) {
pt.x = w->left + (rtl ? w->width - default_width : 0);
pt.y = w->top + w->height;
if (pt.x > _screen.width + 10 - default_width) {
pt.x = _screen.width + 10 - default_width;
}
const Window *wt = FindWindowById(WC_MAIN_TOOLBAR_RIGHT, 0);
if (wt) {
// Move all build toolbar windows to the right, because all build toolbars are always at the right part of the screen
pt.x = _screen.width - wt->width - default_width;
}
return pt;
} else {
/* Position child window with offset of closebox, but make sure that either closebox or resizebox is visible
@@ -1758,7 +1861,7 @@ static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int
* - X position: closebox on left/right, resizebox on right/left (depending on ltr/rtl)
*/
int indent_y = max<int>(NWidgetLeaf::closebox_dimension.height, FONT_HEIGHT_NORMAL + WD_CAPTIONTEXT_TOP + WD_CAPTIONTEXT_BOTTOM);
if (w->top + 3 * indent_y < _screen.height) {
if (w->top + 3 * indent_y < _screen.height && _settings_client.gui.windows_titlebars) {
pt.y = w->top + indent_y;
int indent_close = NWidgetLeaf::closebox_dimension.width;
int indent_resize = NWidgetLeaf::resizebox_dimension.width;
@@ -1794,6 +1897,8 @@ static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int
NOT_REACHED();
}
// try to put it to
return pt;
}
@@ -1871,6 +1976,21 @@ Window *FindWindowFromPt(int x, int y)
return NULL;
}
int SETTING_BUTTON_WIDTH = 20;
int SETTING_BUTTON_HEIGHT = 10;
/**
* Set button size of settings. If automatic sizing is also enabled, it also sets
* the sizing of buttons, scrollbars and font size (recommend restart).
* @todo Check if it can be moved to another file, so we do not need to include error, string and fontcache headers.
* @todo Fix magic numbers 16/18/20/30/32
*/
void CheckWindowMinSizings()
{
SETTING_BUTTON_HEIGHT = GetMinSizing(NWST_STEP);
SETTING_BUTTON_WIDTH = 2 * SETTING_BUTTON_HEIGHT;
}
/**
* (re)initialize the windowing system
*/
@@ -1977,6 +2097,27 @@ static void HandlePlacePresize()
w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
}
/**
* Handle dragging mouse while left button is pressed in no-titlebars mode.
*/
static void HandleMouseDragNoTitlebars()
{
if (_settings_client.gui.windows_titlebars || _dragging_window ||
!_left_button_down || _focused_window == NULL || _dragging_widget) return;
unsigned distance = abs(_cursor.pos.x - _left_button_down_pos.x) + abs(_cursor.pos.y - _left_button_down_pos.y);
if (distance * 2 > GetMinSizing(NWST_STEP) &&
_focused_window->window_class != WC_SELECT_GAME &&
_focused_window->window_class != WC_STATUS_BAR &&
_focused_window->window_class != WC_MAIN_TOOLBAR &&
_focused_window->window_class != WC_MAIN_TOOLBAR_RIGHT &&
_focused_window->window_class != WC_BUILD_CONFIRMATION) {
//SendLeftClickEventToWindow(_focused_window, _left_button_down_pos.x, _left_button_down_pos.y, 1);
StartWindowDrag(_focused_window);
_drag_delta.x += _cursor.pos.x - _left_button_down_pos.x;
_drag_delta.y += _cursor.pos.y - _left_button_down_pos.y;
}
}
/**
* Handle dragging and dropping in mouse dragging mode (#WSM_DRAGDROP).
* @return State of handling the event.
@@ -1985,7 +2126,14 @@ static EventState HandleMouseDragDrop()
{
if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
bool button = _left_button_down;
static bool button_second_click = false;
if (!_settings_client.gui.windows_titlebars) {
if (_left_button_down) button_second_click = true;
button = _left_button_down || !button_second_click;
}
if (button && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; // Dragging, but the mouse did not move.
Window *w = _thd.GetCallbackWnd();
if (w != NULL) {
@@ -1993,14 +2141,18 @@ static EventState HandleMouseDragDrop()
Point pt;
pt.x = _cursor.pos.x - w->left;
pt.y = _cursor.pos.y - w->top;
if (_left_button_down) {
if (button) {
w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
} else {
w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
}
}
if (!_left_button_down) ResetObjectToPlace(); // Button released, finished dragging.
if (!button) {
ResetObjectToPlace(); // Button released, finished dragging.
button_second_click = false;
}
return ES_HANDLED;
}
@@ -2089,7 +2241,7 @@ static void EnsureVisibleCaption(Window *w, int nx, int ny)
/* Search for the title bar rectangle. */
Rect caption_rect;
const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
if (caption != NULL) {
if (caption != NULL && _settings_client.gui.windows_titlebars) {
caption_rect.left = caption->pos_x;
caption_rect.right = caption->pos_x + caption->current_x;
caption_rect.top = caption->pos_y;
@@ -2098,9 +2250,11 @@ static void EnsureVisibleCaption(Window *w, int nx, int ny)
/* Make sure the window doesn't leave the screen */
nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
/* Make sure the title bar isn't hidden behind the main tool bar or the status bar. */
PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
if (!_settings_client.gui.vertical_toolbar || _game_mode == GM_EDITOR) {
// This call hides the window totally with vertical toolbar if you move it slightly off-screen to the left
PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
}
PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
}
@@ -2161,6 +2315,7 @@ void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
*/
int GetMainViewTop()
{
if (_settings_client.gui.vertical_toolbar && _game_mode != GM_EDITOR) return 0;
Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
return (w == NULL) ? 0 : w->top + w->height;
}
@@ -2176,7 +2331,31 @@ int GetMainViewBottom()
return (w == NULL) ? _screen.height : w->top;
}
static bool _dragging_window; ///< A window is being dragged or resized.
bool GetWindowDraggedOffScreen(const Window *w)
{
if (_settings_client.gui.windows_titlebars) return false;
Rect edge = { 0, GetMainViewTop(), _screen.width, _screen.height };
if (_settings_client.gui.vertical_toolbar && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
edge.left += GetMinSizing(NWST_BUTTON);
edge.right -= GetMinSizing(NWST_BUTTON);
}
Rect visible = { edge.left + (int)GetMinSizing(NWST_BUTTON) * 4, edge.top + (int)GetMinSizing(NWST_BUTTON) * 2,
edge.right - (int)GetMinSizing(NWST_BUTTON) * 4, edge.bottom - (int)GetMinSizing(NWST_BUTTON) * 2 };
// 1/4 of the window must be hidden to close it when flicking it off to the left/right
if (w->width >= (int)GetMinSizing(NWST_BUTTON) * 4) {
edge.left -= GetMinSizing(NWST_BUTTON) * 2;
edge.right += GetMinSizing(NWST_BUTTON) * 2;
}
if (w->height >= (int)GetMinSizing(NWST_BUTTON) * 4) {
edge.top -= GetMinSizing(NWST_BUTTON);
edge.bottom += GetMinSizing(NWST_BUTTON);
}
if (w->left < edge.left && w->left + w->width < visible.right) return true;
if (w->left + w->width > edge.right && w->left > visible.left) return true;
if (w->top < edge.top && w->top + w->height < visible.bottom) return true;
if (w->top + w->height > edge.bottom && w->top > visible.top) return true;
return false;
}
/**
* Handle dragging/resizing of a window.
@@ -2197,6 +2376,9 @@ static EventState HandleWindowDragging()
/* Stop the dragging if the left mouse button was released */
if (!_left_button_down) {
w->flags &= ~WF_DRAGGING;
if (GetWindowDraggedOffScreen(w)) {
delete w;
}
break;
}
@@ -2286,6 +2468,12 @@ static EventState HandleWindowDragging()
EnsureVisibleCaption(w, nx, ny);
w->SetDirty();
if (GetWindowDraggedOffScreen(w)) {
GuiShowTooltips(w, STR_TOOLTIP_CLOSE_WINDOW, 0, NULL, TCC_LEFT_CLICK);
} else {
GuiShowTooltips(w, STR_NULL, 0, NULL, TCC_LEFT_CLICK); // Hide tooltip
}
return ES_HANDLED;
} else if (w->flags & WF_SIZING) {
/* Stop the sizing if the left mouse button was released */
@@ -2406,7 +2594,7 @@ static void HandleScrollbarScrolling(Window *w)
if (sb->disp_flags & ND_SCROLLBAR_BTN) {
if (_scroller_click_timeout == 1) {
_scroller_click_timeout = 3;
_scroller_click_timeout = SCROLLER_CLICK_DELAY;
sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
w->SetDirty();
}
@@ -2464,6 +2652,21 @@ static EventState HandleViewportScroll()
{
bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
if (_settings_client.gui.scroll_mode == VSM_MAP_LMB) {
// Do not open vehicle/town info window while scrolling with left mouse button
static int oldDx = 0, oldDy = 0;
if (_left_button_down) {
oldDx += _cursor.delta.x;
oldDy += _cursor.delta.y;
if (!_left_button_dragged && (abs(oldDx) + abs(oldDy)) * 2 > (int)GetMinSizing(NWST_STEP, 10)) {
_left_button_dragged = true;
}
} else {
oldDx = 0;
oldDy = 0;
}
}
if (!_scrolling_viewport) return ES_NOT_HANDLED;
/* When we don't have a last scroll window we are starting to scroll.
@@ -2471,7 +2674,7 @@ static EventState HandleViewportScroll()
* outside of the window and should not left-mouse scroll anymore. */
if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
if (_last_scroll_window == NULL || !((_settings_client.gui.scroll_mode != VSM_MAP_LMB && _right_button_down) || scrollwheel_scrolling || (_settings_client.gui.scroll_mode == VSM_MAP_LMB && _left_button_down))) {
if (_last_scroll_window == NULL || !((_settings_client.gui.scroll_mode != VSM_MAP_LMB && _right_button_down) || scrollwheel_scrolling || ((_move_pressed || _settings_client.gui.scroll_mode == VSM_MAP_LMB) && _left_button_down))) {
_cursor.fix_at = false;
_scrolling_viewport = false;
_last_scroll_window = NULL;
@@ -2760,12 +2963,14 @@ static int _input_events_this_tick = 0;
*/
static void HandleAutoscroll()
{
_move_pressed = false;
if (_game_mode == GM_MENU || HasModalProgress()) return;
if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
int x = _cursor.pos.x;
int y = _cursor.pos.y;
int border = RescaleFrom854x480(_settings_client.gui.min_button);
Window *w = FindWindowFromPt(x, y);
if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
@@ -2777,22 +2982,44 @@ static void HandleAutoscroll()
y -= vp->top;
/* here allows scrolling in both x and y axis */
static const int SCROLLSPEED = 3;
if (x - 15 < 0) {
w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * SCROLLSPEED, vp->zoom);
} else if (15 - (vp->width - x) > 0) {
w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * SCROLLSPEED, vp->zoom);
static const int SCROLLSPEED = 15;
if (x - border < 0) {
_move_pressed = true;
w->viewport->dest_scrollpos_x += ScaleByZoom((x - border) * SCROLLSPEED, vp->zoom);
} else if (border - (vp->width - x) > 0) {
_move_pressed = true;
w->viewport->dest_scrollpos_x += ScaleByZoom((border - (vp->width - x)) * SCROLLSPEED, vp->zoom);
}
if (y - 15 < 0) {
w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * SCROLLSPEED, vp->zoom);
} else if (15 - (vp->height - y) > 0) {
w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * SCROLLSPEED, vp->zoom);
if (y - border * 2 < 0) { // Border twice thicker, to accomodate top toolbar
_move_pressed = true;
w->viewport->dest_scrollpos_y += ScaleByZoom((y - border * 2) * SCROLLSPEED, vp->zoom);
} else if (border - (vp->height - y) > 0) {
_move_pressed = true;
w->viewport->dest_scrollpos_y += ScaleByZoom((border - (vp->height - y)) * SCROLLSPEED, vp->zoom);
}
}
/**
* Perform small continuous scrolling with right button press and drag.
*/
static void HandleContinuousScroll()
{
static const float scrollspeed = 0.05f;
if (_scrolling_viewport && _right_button_down) {
Window *w = FindWindowFromPt(_right_button_down_pos.x, _right_button_down_pos.y);
if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
ViewPort *vp = IsPtInWindowViewport(w, _right_button_down_pos.x, _right_button_down_pos.y);
if (vp == NULL) return;
w->viewport->dest_scrollpos_x += ScaleByZoom(scrollspeed * (_right_button_down_pos.x - _cursor.pos.x), vp->zoom);
w->viewport->dest_scrollpos_y += ScaleByZoom(scrollspeed * (_right_button_down_pos.y - _cursor.pos.y), vp->zoom);
}
}
enum MouseClick {
MC_NONE = 0,
MC_LEFT,
MC_LEFT_UP,
MC_RIGHT,
MC_DOUBLE_LEFT,
MC_HOVER,
@@ -2860,8 +3087,21 @@ static void MouseLoop(MouseClick click, int mousewheel)
* But there is no company related window open anyway, so _current_company is not used. */
assert(HasModalProgress() || IsLocalCompany());
static bool mouse_down_on_viewport = false;
int x = _cursor.pos.x;
int y = _cursor.pos.y;
Window *w = FindWindowFromPt(x, y);
if (w == NULL) return;
ViewPort *vp = IsPtInWindowViewport(w, x, y);
/* Don't allow any action in a viewport if either in menu or when having a modal progress window */
if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
if (click == MC_LEFT) _left_button_down_pos = Point { x, y };
HandlePlacePresize();
UpdateTileSelection();
if (!mouse_down_on_viewport) HandleMouseDragNoTitlebars();
if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
if (HandleMouseDragDrop() == ES_HANDLED) return;
@@ -2874,16 +3114,9 @@ static void MouseLoop(MouseClick click, int mousewheel)
bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
int x = _cursor.pos.x;
int y = _cursor.pos.y;
Window *w = FindWindowFromPt(x, y);
if (w == NULL) return;
if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
ViewPort *vp = IsPtInWindowViewport(w, x, y);
/* Don't allow any action in a viewport if either in menu or when having a modal progress window */
if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
if (click != MC_NONE && click != MC_HOVER && click != MC_LEFT_UP && !MaybeBringWindowToFront(w)) return;
if (mousewheel != 0) {
/* Send mousewheel event to window, unless we're scrolling a viewport or the map */
@@ -2903,14 +3136,32 @@ static void MouseLoop(MouseClick click, int mousewheel)
switch (click) {
case MC_DOUBLE_LEFT:
case MC_LEFT:
if (HandleViewportClicked(vp, x, y)) return;
if (!(w->flags & WF_DISABLE_VP_SCROLL) &&
_settings_client.gui.scroll_mode == VSM_MAP_LMB) {
_scrolling_viewport = true;
_cursor.fix_at = false;
mouse_down_on_viewport = true;
if (HandleViewportClicked(vp, x, y)) {
// Viewport already clicked, prevent sending same event on mouse-up
_left_button_dragged = true;
return;
}
break;
if (!(w->flags & WF_DISABLE_VP_SCROLL) &&
(_settings_client.gui.scroll_mode == VSM_MAP_LMB || _move_pressed)) {
_scrolling_viewport = true;
_cursor.fix_at = false;
} else {
// Viewport already clicked, prevent sending same event on mouse-up
_left_button_dragged = true;
}
return;
case MC_LEFT_UP:
if (!_left_button_dragged && mouse_down_on_viewport) {
HandleViewportMouseUp(vp, x, y);
MoveAllHiddenWindowsBackToScreen();
} else if (_left_button_dragged && mouse_down_on_viewport) {
BuildConfirmationWindowProcessViewportClick();
}
_left_button_dragged = false;
mouse_down_on_viewport = false;
return;
case MC_RIGHT:
if (!(w->flags & WF_DISABLE_VP_SCROLL) &&
@@ -2929,9 +3180,14 @@ static void MouseLoop(MouseClick click, int mousewheel)
if (vp == NULL || (w->flags & WF_DISABLE_VP_SCROLL)) {
switch (click) {
case MC_LEFT_UP:
DispatchLeftButtonUpEvent(w, x - w->left, y - w->top);
mouse_down_on_viewport = false;
break;
case MC_LEFT:
case MC_DOUBLE_LEFT:
DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
DispatchLeftButtonDownEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
return;
default:
@@ -2966,6 +3222,7 @@ void HandleMouseEvents()
static int double_click_time = 0;
static Point double_click_pos = {0, 0};
static bool left_button_released = false;
/* Mouse event? */
MouseClick click = MC_NONE;
@@ -2979,11 +3236,17 @@ void HandleMouseEvents()
double_click_time = _realtime_tick;
double_click_pos = _cursor.pos;
_left_button_clicked = true;
left_button_released = false;
_input_events_this_tick++;
} else if (_right_button_clicked) {
_right_button_clicked = false;
click = MC_RIGHT;
_input_events_this_tick++;
} else if(!_left_button_down && !left_button_released) {
click = MC_LEFT_UP;
left_button_released = true;
_left_button_clicked = false;
_input_events_this_tick++;
}
int mousewheel = 0;
@@ -3093,6 +3356,8 @@ void InputLoop()
/* HandleMouseEvents was already called for this tick */
HandleMouseEvents();
HandleAutoscroll();
HandleContinuousScroll();
}
/**
@@ -3356,6 +3621,7 @@ restart_search:
if (w->window_class != WC_MAIN_WINDOW &&
w->window_class != WC_SELECT_GAME &&
w->window_class != WC_MAIN_TOOLBAR &&
w->window_class != WC_MAIN_TOOLBAR_RIGHT &&
w->window_class != WC_STATUS_BAR &&
w->window_class != WC_TOOLTIPS &&
(w->flags & WF_STICKY) == 0) { // do not delete windows which are 'pinned'
@@ -3425,10 +3691,33 @@ restart_search:
FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
}
/**
* Delete all windows that are linked to the main toolbar.
* Once done with that, refresh other windows too.
*/
void DeleteToolbarLinkedWindows()
{
Window *w;
restart_search:
/* When we find the window to delete, we need to restart the search
* as deleting this window could cascade in deleting (many) others
* anywhere in the z-array */
FOR_ALL_WINDOWS_FROM_BACK(w) {
if (w->window_desc->default_pos == WDP_ALIGN_TOOLBAR) {
delete w;
goto restart_search;
}
}
FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
}
/** Delete all always on-top windows to get an empty screen */
void HideVitalWindows()
{
DeleteWindowById(WC_MAIN_TOOLBAR, 0);
DeleteWindowById(WC_MAIN_TOOLBAR_RIGHT, 0);
DeleteWindowById(WC_STATUS_BAR, 0);
}
@@ -3487,6 +3776,7 @@ static int PositionWindow(Window *w, WindowClass clss, int setting)
*/
int PositionMainToolbar(Window *w)
{
if (_settings_client.gui.vertical_toolbar && _game_mode != GM_EDITOR) return 0; /* Always at the left */
DEBUG(misc, 5, "Repositioning Main Toolbar...");
return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
}
@@ -3569,13 +3859,20 @@ void RelocateAllWindows(int neww, int newh)
left = PositionMainToolbar(w); // changes toolbar orientation
break;
case WC_MAIN_TOOLBAR_RIGHT:
ResizeWindow(w, min(neww, _toolbar_width) - w->width, 0, false);
top = w->top;
left = neww - w->width;
break;
case WC_NEWS_WINDOW:
top = newh - w->height;
left = PositionNewsMessage(w);
break;
case WC_STATUS_BAR:
ResizeWindow(w, min(neww, _toolbar_width) - w->width, 0, false);
ResizeWindow(w, min(neww, min(_toolbar_width, _screen.width - SETTING_BUTTON_HEIGHT * 2)) - w->width, 0, false);
top = newh - w->height;
left = PositionStatusbar(w);
@@ -3613,6 +3910,65 @@ void RelocateAllWindows(int neww, int newh)
}
}
static void MoveAllWindowsOffScreen(bool moveOffScreen)
{
Window *w;
bool updateScreen = false;
FOR_ALL_WINDOWS_FROM_BACK(w) {
switch (w->window_class) {
case WC_MAIN_WINDOW:
case WC_BUILD_CONFIRMATION:
case WC_BOOTSTRAP:
case WC_MAIN_TOOLBAR:
case WC_MAIN_TOOLBAR_RIGHT:
case WC_NEWS_WINDOW:
case WC_STATUS_BAR:
case WC_SEND_NETWORK_MSG:
case WC_CONSOLE:
continue;
default:
if (moveOffScreen) {
if (w->left < _screen.width) {
w->left += _screen.width;
if (w->viewport != NULL) {
w->viewport->left += _screen.width;
}
//w->SetDirty();
updateScreen = true;
}
} else {
if (w->left >= _screen.width) {
w->left -= _screen.width;
if (w->viewport != NULL) {
w->viewport->left -= _screen.width;
}
w->SetDirty();
//updateScreen = true;
}
}
break;
}
}
if (updateScreen) {
w = FindWindowById(WC_MAIN_WINDOW, 0);
if (w) {
w->SetDirty();
}
}
}
void MoveAllWindowsOffScreen()
{
MoveAllWindowsOffScreen(true);
}
void MoveAllHiddenWindowsBackToScreen()
{
MoveAllWindowsOffScreen(false);
}
/**
* Destructor of the base class PickerWindowBase
* Main utility is to stop the base Window destructor from triggering