src/window.cpp
branchNewGRF_ports
changeset 10991 d8811e327d12
parent 10731 67db0d431d5e
child 10994 cd9968b6f96b
equal deleted inserted replaced
10731:67db0d431d5e 10991:d8811e327d12
    35  * bottom window is at _z_windows[0]
    35  * bottom window is at _z_windows[0]
    36  */
    36  */
    37 Window *_z_windows[MAX_NUMBER_OF_WINDOWS];
    37 Window *_z_windows[MAX_NUMBER_OF_WINDOWS];
    38 Window **_last_z_window; ///< always points to the next free space in the z-array
    38 Window **_last_z_window; ///< always points to the next free space in the z-array
    39 
    39 
       
    40 byte _no_scroll;
    40 Point _cursorpos_drag_start;
    41 Point _cursorpos_drag_start;
    41 
    42 
    42 int _scrollbar_start_pos;
    43 int _scrollbar_start_pos;
    43 int _scrollbar_size;
    44 int _scrollbar_size;
    44 byte _scroller_click_timeout;
    45 byte _scroller_click_timeout;
   718 	*wz = this;
   719 	*wz = this;
   719 	_last_z_window++;
   720 	_last_z_window++;
   720 }
   721 }
   721 
   722 
   722 /**
   723 /**
   723  * Find a nice spot for this window and resize it towards the default size.
   724  * Resize window towards the default size.
       
   725  * Prior to construction, a position for the new window (for its default size)
       
   726  * has been found with LocalGetWindowPlacement(). Initially, the window is
       
   727  * constructed with minimal size. Resizing the window to its default size is
       
   728  * done here.
   724  * @param def_width default width in pixels of the window
   729  * @param def_width default width in pixels of the window
   725  * @param def_height default height in pixels of the window
   730  * @param def_height default height in pixels of the window
       
   731  * @see Window::Window(), Window::Initialize()
   726  */
   732  */
   727 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
   733 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
   728 {
   734 {
   729 	/* Try to make windows smaller when our window is too small.
   735 	/* Try to make windows smaller when our window is too small.
   730 	 * w->(width|height) is normally the same as min_(width|height),
   736 	 * w->(width|height) is normally the same as min_(width|height),
   799 Window::Window(int x, int y, int width, int height, WindowClass cls, const Widget *widget)
   805 Window::Window(int x, int y, int width, int height, WindowClass cls, const Widget *widget)
   800 {
   806 {
   801 	this->Initialize(x, y, width, height, cls, widget, 0);
   807 	this->Initialize(x, y, width, height, cls, widget, 0);
   802 }
   808 }
   803 
   809 
   804 
   810 /**
       
   811  * Decide whether a given rectangle is a good place to open a completely visible new window.
       
   812  * The new window should be within screen borders, and not overlap with another already
       
   813  * existing window (except for the main window in the background).
       
   814  * @param left    Left edge of the rectangle
       
   815  * @param top     Top edge of the rectangle
       
   816  * @param width   Width of the rectangle
       
   817  * @param height  Height of the rectangle
       
   818  * @param pos     If rectangle is good, use this parameter to return the top-left corner of the new window
       
   819  * @return Boolean indication that the rectangle is a good place for the new window
       
   820  */
   805 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
   821 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
   806 {
   822 {
   807 	Window* const *wz;
   823 	Window* const *wz;
   808 
   824 
   809 	int right  = width + left;
   825 	int right  = width + left;
   827 	pos.x = left;
   843 	pos.x = left;
   828 	pos.y = top;
   844 	pos.y = top;
   829 	return true;
   845 	return true;
   830 }
   846 }
   831 
   847 
       
   848 /**
       
   849  * Decide whether a given rectangle is a good place to open a mostly visible new window.
       
   850  * The new window should be mostly within screen borders, and not overlap with another already
       
   851  * existing window (except for the main window in the background).
       
   852  * @param left    Left edge of the rectangle
       
   853  * @param top     Top edge of the rectangle
       
   854  * @param width   Width of the rectangle
       
   855  * @param height  Height of the rectangle
       
   856  * @param pos     If rectangle is good, use this parameter to return the top-left corner of the new window
       
   857  * @return Boolean indication that the rectangle is a good place for the new window
       
   858  */
   832 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
   859 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
   833 {
   860 {
   834 	Window* const *wz;
   861 	Window* const *wz;
   835 
   862 
       
   863 	/* Left part of the rectangle may be at most 1/4 off-screen,
       
   864 	 * right part of the rectangle may be at most 1/2 off-screen
       
   865 	 */
   836 	if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
   866 	if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
       
   867 	/* Bottom part of the rectangle may be at most 1/4 off-screen */
   837 	if (top < 22 || top > _screen.height - (height>>2)) return false;
   868 	if (top < 22 || top > _screen.height - (height>>2)) return false;
   838 
   869 
   839 	/* Make sure it is not obscured by any window. */
   870 	/* Make sure it is not obscured by any window. */
   840 	FOR_ALL_WINDOWS(wz) {
   871 	FOR_ALL_WINDOWS(wz) {
   841 		const Window *w = *wz;
   872 		const Window *w = *wz;
   852 	pos.x = left;
   883 	pos.x = left;
   853 	pos.y = top;
   884 	pos.y = top;
   854 	return true;
   885 	return true;
   855 }
   886 }
   856 
   887 
       
   888 /**
       
   889  * Find a good place for opening a new window of a given width and height.
       
   890  * @param width  Width of the new window
       
   891  * @param height Height of the new window
       
   892  * @return Top-left coordinate of the new window
       
   893  */
   857 static Point GetAutoPlacePosition(int width, int height)
   894 static Point GetAutoPlacePosition(int width, int height)
   858 {
   895 {
   859 	Window* const *wz;
   896 	Window* const *wz;
   860 	Point pt;
   897 	Point pt;
   861 
   898 
       
   899 	/* First attempt, try top-left of the screen */
   862 	if (IsGoodAutoPlace1(0, 24, width, height, pt)) return pt;
   900 	if (IsGoodAutoPlace1(0, 24, width, height, pt)) return pt;
   863 
   901 
       
   902 	/* Second attempt, try around all existing windows with a distance of 2 pixels.
       
   903 	 * The new window must be entirely on-screen, and not overlap with an existing window.
       
   904 	 * Eight starting points are tried, two at each corner.
       
   905 	 */
   864 	FOR_ALL_WINDOWS(wz) {
   906 	FOR_ALL_WINDOWS(wz) {
   865 		const Window *w = *wz;
   907 		const Window *w = *wz;
   866 		if (w->window_class == WC_MAIN_WINDOW) continue;
   908 		if (w->window_class == WC_MAIN_WINDOW) continue;
   867 
   909 
   868 		if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
   910 		if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
   873 		if (IsGoodAutoPlace1(w->left - width - 2,    w->top + w->height - height, width, height, pt)) return pt;
   915 		if (IsGoodAutoPlace1(w->left - width - 2,    w->top + w->height - height, width, height, pt)) return pt;
   874 		if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
   916 		if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
   875 		if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2,    width, height, pt)) return pt;
   917 		if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2,    width, height, pt)) return pt;
   876 	}
   918 	}
   877 
   919 
       
   920 	/* Third attempt, try around all existing windows with a distance of 2 pixels.
       
   921 	 * The new window may be partly off-screen, and must not overlap with an existing window.
       
   922 	 * Only four starting points are tried.
       
   923 	 */
   878 	FOR_ALL_WINDOWS(wz) {
   924 	FOR_ALL_WINDOWS(wz) {
   879 		const Window *w = *wz;
   925 		const Window *w = *wz;
   880 		if (w->window_class == WC_MAIN_WINDOW) continue;
   926 		if (w->window_class == WC_MAIN_WINDOW) continue;
   881 
   927 
   882 		if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
   928 		if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
   883 		if (IsGoodAutoPlace2(w->left - width - 2,    w->top, width, height, pt)) return pt;
   929 		if (IsGoodAutoPlace2(w->left - width - 2,    w->top, width, height, pt)) return pt;
   884 		if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
   930 		if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
   885 		if (IsGoodAutoPlace2(w->left, w->top - height - 2,    width, height, pt)) return pt;
   931 		if (IsGoodAutoPlace2(w->left, w->top - height - 2,    width, height, pt)) return pt;
   886 	}
   932 	}
   887 
   933 
       
   934 	/* Fourth and final attempt, put window at diagonal starting from (0, 24), try multiples
       
   935 	 * of (+5, +5)
       
   936 	 */
   888 	{
   937 	{
   889 		int left = 0, top = 24;
   938 		int left = 0, top = 24;
   890 
   939 
   891 restart:
   940 restart:
   892 		FOR_ALL_WINDOWS(wz) {
   941 		FOR_ALL_WINDOWS(wz) {
  1019 	IConsoleClose();
  1068 	IConsoleClose();
  1020 
  1069 
  1021 	_last_z_window = _z_windows;
  1070 	_last_z_window = _z_windows;
  1022 	_mouseover_last_w = NULL;
  1071 	_mouseover_last_w = NULL;
  1023 	_no_scroll = 0;
  1072 	_no_scroll = 0;
       
  1073 	_scrolling_viewport = 0;
  1024 }
  1074 }
  1025 
  1075 
  1026 /**
  1076 /**
  1027  * Close down the windowing system
  1077  * Close down the windowing system
  1028  */
  1078  */
  1215 			int x = _cursor.pos.x + _drag_delta.x;
  1265 			int x = _cursor.pos.x + _drag_delta.x;
  1216 			int y = _cursor.pos.y + _drag_delta.y;
  1266 			int y = _cursor.pos.y + _drag_delta.y;
  1217 			int nx = x;
  1267 			int nx = x;
  1218 			int ny = y;
  1268 			int ny = y;
  1219 
  1269 
  1220 			if (_settings.gui.window_snap_radius != 0) {
  1270 			if (_settings_client.gui.window_snap_radius != 0) {
  1221 				Window* const *vz;
  1271 				Window* const *vz;
  1222 
  1272 
  1223 				int hsnap = _settings.gui.window_snap_radius;
  1273 				int hsnap = _settings_client.gui.window_snap_radius;
  1224 				int vsnap = _settings.gui.window_snap_radius;
  1274 				int vsnap = _settings_client.gui.window_snap_radius;
  1225 				int delta;
  1275 				int delta;
  1226 
  1276 
  1227 				FOR_ALL_WINDOWS(vz) {
  1277 				FOR_ALL_WINDOWS(vz) {
  1228 					const Window *v = *vz;
  1278 					const Window *v = *vz;
  1229 
  1279 
  1465 	return false;
  1515 	return false;
  1466 }
  1516 }
  1467 
  1517 
  1468 static bool HandleViewportScroll()
  1518 static bool HandleViewportScroll()
  1469 {
  1519 {
  1470 	bool scrollwheel_scrolling = _settings.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
  1520 	bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
  1471 
  1521 
  1472 	if (!_scrolling_viewport) return true;
  1522 	if (!_scrolling_viewport) return true;
  1473 
  1523 
  1474 	Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
  1524 	Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
  1475 
  1525 
  1485 		ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */
  1535 		ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */
  1486 		return true;
  1536 		return true;
  1487 	}
  1537 	}
  1488 
  1538 
  1489 	Point delta;
  1539 	Point delta;
  1490 	if (_settings.gui.reverse_scroll) {
  1540 	if (_settings_client.gui.reverse_scroll) {
  1491 		delta.x = -_cursor.delta.x;
  1541 		delta.x = -_cursor.delta.x;
  1492 		delta.y = -_cursor.delta.y;
  1542 		delta.y = -_cursor.delta.y;
  1493 	} else {
  1543 	} else {
  1494 		delta.x = _cursor.delta.x;
  1544 		delta.x = _cursor.delta.x;
  1495 		delta.y = _cursor.delta.y;
  1545 		delta.y = _cursor.delta.y;
  1665 		_input_events_this_tick = 0;
  1715 		_input_events_this_tick = 0;
  1666 		/* there were some inputs this tick, don't scroll ??? */
  1716 		/* there were some inputs this tick, don't scroll ??? */
  1667 		return;
  1717 		return;
  1668 	}
  1718 	}
  1669 
  1719 
  1670 	if (_settings.gui.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
  1720 	if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
  1671 		int x = _cursor.pos.x;
  1721 		int x = _cursor.pos.x;
  1672 		int y = _cursor.pos.y;
  1722 		int y = _cursor.pos.y;
  1673 		Window *w = FindWindowFromPt(x, y);
  1723 		Window *w = FindWindowFromPt(x, y);
  1674 		if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
  1724 		if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
  1675 		ViewPort *vp = IsPtInWindowViewport(w, x, y);
  1725 		ViewPort *vp = IsPtInWindowViewport(w, x, y);
  1705 };
  1755 };
  1706 
  1756 
  1707 extern void UpdateTileSelection();
  1757 extern void UpdateTileSelection();
  1708 extern bool VpHandlePlaceSizingDrag();
  1758 extern bool VpHandlePlaceSizingDrag();
  1709 
  1759 
       
  1760 static void ScrollMainViewport(int x, int y)
       
  1761 {
       
  1762 	if (_game_mode != GM_MENU) {
       
  1763 		Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
       
  1764 		assert(w);
       
  1765 
       
  1766 		w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
       
  1767 		w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
       
  1768 	}
       
  1769 }
       
  1770 
       
  1771 /**
       
  1772  * Describes all the different arrow key combinations the game allows
       
  1773  * when it is in scrolling mode.
       
  1774  * The real arrow keys are bitwise numbered as
       
  1775  * 1 = left
       
  1776  * 2 = up
       
  1777  * 4 = right
       
  1778  * 8 = down
       
  1779  */
       
  1780 static const int8 scrollamt[16][2] = {
       
  1781 	{ 0,  0}, ///<  no key specified
       
  1782 	{-2,  0}, ///<  1 : left
       
  1783 	{ 0, -2}, ///<  2 : up
       
  1784 	{-2, -1}, ///<  3 : left  + up
       
  1785 	{ 2,  0}, ///<  4 : right
       
  1786 	{ 0,  0}, ///<  5 : left  + right = nothing
       
  1787 	{ 2, -1}, ///<  6 : right + up
       
  1788 	{ 0, -2}, ///<  7 : right + left  + up = up
       
  1789 	{ 0  ,2}, ///<  8 : down
       
  1790 	{-2  ,1}, ///<  9 : down  + left
       
  1791 	{ 0,  0}, ///< 10 : down  + up    = nothing
       
  1792 	{-2,  0}, ///< 11 : left  + up    +  down = left
       
  1793 	{ 2,  1}, ///< 12 : down  + right
       
  1794 	{ 0,  2}, ///< 13 : left  + right +  down = down
       
  1795 	{ 2,  0}, ///< 14 : right + up    +  down = right
       
  1796 	{ 0,  0}, ///< 15 : left  + up    +  right + down  = nothing
       
  1797 };
       
  1798 
       
  1799 static void HandleKeyScrolling()
       
  1800 {
       
  1801 	if (_dirkeys && !_no_scroll) {
       
  1802 		int factor = _shift_pressed ? 50 : 10;
       
  1803 		ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
       
  1804 	}
       
  1805 }
       
  1806 
  1710 void MouseLoop(MouseClick click, int mousewheel)
  1807 void MouseLoop(MouseClick click, int mousewheel)
  1711 {
  1808 {
  1712 	DecreaseWindowCounters();
  1809 	DecreaseWindowCounters();
  1713 	HandlePlacePresize();
  1810 	HandlePlacePresize();
  1714 	UpdateTileSelection();
  1811 	UpdateTileSelection();
       
  1812 	HandleKeyScrolling();
       
  1813 
  1715 	if (!VpHandlePlaceSizingDrag())  return;
  1814 	if (!VpHandlePlaceSizingDrag())  return;
  1716 	if (!HandleDragDrop())           return;
  1815 	if (!HandleDragDrop())           return;
  1717 	if (!HandleWindowDragging())     return;
  1816 	if (!HandleWindowDragging())     return;
  1718 	if (!HandleScrollbarScrolling()) return;
  1817 	if (!HandleScrollbarScrolling()) return;
  1719 	if (!HandleViewportScroll())     return;
  1818 	if (!HandleViewportScroll())     return;
  1720 	if (!HandleMouseOver())          return;
  1819 	if (!HandleMouseOver())          return;
  1721 
  1820 
  1722 	bool scrollwheel_scrolling = _settings.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
  1821 	bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
  1723 	if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
  1822 	if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
  1724 
  1823 
  1725 	int x = _cursor.pos.x;
  1824 	int x = _cursor.pos.x;
  1726 	int y = _cursor.pos.y;
  1825 	int y = _cursor.pos.y;
  1727 	Window *w = FindWindowFromPt(x, y);
  1826 	Window *w = FindWindowFromPt(x, y);
  1732 
  1831 
  1733 	/* Don't allow any action in a viewport if either in menu of in generating world */
  1832 	/* Don't allow any action in a viewport if either in menu of in generating world */
  1734 	if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
  1833 	if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
  1735 
  1834 
  1736 	if (mousewheel != 0) {
  1835 	if (mousewheel != 0) {
  1737 		if (_settings.gui.scrollwheel_scrolling == 0) {
  1836 		if (_settings_client.gui.scrollwheel_scrolling == 0) {
  1738 			/* Send mousewheel event to window */
  1837 			/* Send mousewheel event to window */
  1739 			w->OnMouseWheel(mousewheel);
  1838 			w->OnMouseWheel(mousewheel);
  1740 		}
  1839 		}
  1741 
  1840 
  1742 		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
  1841 		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
  1977 /**
  2076 /**
  1978  * Dispatch WE_TICK event over all windows
  2077  * Dispatch WE_TICK event over all windows
  1979  */
  2078  */
  1980 void CallWindowTickEvent()
  2079 void CallWindowTickEvent()
  1981 {
  2080 {
       
  2081 	if (_scroller_click_timeout > 3) {
       
  2082 		_scroller_click_timeout -= 3;
       
  2083 	} else {
       
  2084 		_scroller_click_timeout = 0;
       
  2085 	}
       
  2086 
  1982 	for (Window * const *wz = _last_z_window; wz != _z_windows;) {
  2087 	for (Window * const *wz = _last_z_window; wz != _z_windows;) {
  1983 		(*--wz)->OnTick();
  2088 		(*--wz)->OnTick();
  1984 	}
  2089 	}
  1985 }
  2090 }
  1986 
  2091 
  2057 
  2162 
  2058 	if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
  2163 	if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
  2059 		w = FindWindowById(WC_MAIN_TOOLBAR, 0);
  2164 		w = FindWindowById(WC_MAIN_TOOLBAR, 0);
  2060 	}
  2165 	}
  2061 
  2166 
  2062 	switch (_settings.gui.toolbar_pos) {
  2167 	switch (_settings_client.gui.toolbar_pos) {
  2063 		case 1:  w->left = (_screen.width - w->width) / 2; break;
  2168 		case 1:  w->left = (_screen.width - w->width) / 2; break;
  2064 		case 2:  w->left = _screen.width - w->width; break;
  2169 		case 2:  w->left = _screen.width - w->width; break;
  2065 		default: w->left = 0;
  2170 		default: w->left = 0;
  2066 	}
  2171 	}
  2067 	SetDirtyBlocks(0, 0, _screen.width, w->height); // invalidate the whole top part
  2172 	SetDirtyBlocks(0, 0, _screen.width, w->height); // invalidate the whole top part
  2068 	return w->left;
  2173 	return w->left;
       
  2174 }
       
  2175 
       
  2176 void SetVScrollCount(Window *w, int num)
       
  2177 {
       
  2178 	w->vscroll.count = num;
       
  2179 	num -= w->vscroll.cap;
       
  2180 	if (num < 0) num = 0;
       
  2181 	if (num < w->vscroll.pos) w->vscroll.pos = num;
       
  2182 }
       
  2183 
       
  2184 void SetVScroll2Count(Window *w, int num)
       
  2185 {
       
  2186 	w->vscroll2.count = num;
       
  2187 	num -= w->vscroll2.cap;
       
  2188 	if (num < 0) num = 0;
       
  2189 	if (num < w->vscroll2.pos) w->vscroll2.pos = num;
       
  2190 }
       
  2191 
       
  2192 void SetHScrollCount(Window *w, int num)
       
  2193 {
       
  2194 	w->hscroll.count = num;
       
  2195 	num -= w->hscroll.cap;
       
  2196 	if (num < 0) num = 0;
       
  2197 	if (num < w->hscroll.pos) w->hscroll.pos = num;
  2069 }
  2198 }
  2070 
  2199 
  2071 /**
  2200 /**
  2072  * Relocate all windows to fit the new size of the game application screen
  2201  * Relocate all windows to fit the new size of the game application screen
  2073  * @param neww New width of the game application screen
  2202  * @param neww New width of the game application screen