Merge remote-tracking branch 'origin/master' into HEAD
This commit is contained in:
458
src/window.cpp
458
src/window.cpp
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user