diff -r 67db0d431d5e -r d8811e327d12 src/window.cpp --- a/src/window.cpp Tue May 27 00:50:55 2008 +0000 +++ b/src/window.cpp Tue Jun 17 10:32:49 2008 +0000 @@ -37,6 +37,7 @@ Window *_z_windows[MAX_NUMBER_OF_WINDOWS]; Window **_last_z_window; ///< always points to the next free space in the z-array +byte _no_scroll; Point _cursorpos_drag_start; int _scrollbar_start_pos; @@ -720,9 +721,14 @@ } /** - * Find a nice spot for this window and resize it towards the default size. + * Resize window towards the default size. + * Prior to construction, a position for the new window (for its default size) + * has been found with LocalGetWindowPlacement(). Initially, the window is + * constructed with minimal size. Resizing the window to its default size is + * done here. * @param def_width default width in pixels of the window * @param def_height default height in pixels of the window + * @see Window::Window(), Window::Initialize() */ void Window::FindWindowPlacementAndResize(int def_width, int def_height) { @@ -801,7 +807,17 @@ this->Initialize(x, y, width, height, cls, widget, 0); } - +/** + * Decide whether a given rectangle is a good place to open a completely visible new window. + * The new window should be within screen borders, and not overlap with another already + * existing window (except for the main window in the background). + * @param left Left edge of the rectangle + * @param top Top edge of the rectangle + * @param width Width of the rectangle + * @param height Height of the rectangle + * @param pos If rectangle is good, use this parameter to return the top-left corner of the new window + * @return Boolean indication that the rectangle is a good place for the new window + */ static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos) { Window* const *wz; @@ -829,11 +845,26 @@ return true; } +/** + * Decide whether a given rectangle is a good place to open a mostly visible new window. + * The new window should be mostly within screen borders, and not overlap with another already + * existing window (except for the main window in the background). + * @param left Left edge of the rectangle + * @param top Top edge of the rectangle + * @param width Width of the rectangle + * @param height Height of the rectangle + * @param pos If rectangle is good, use this parameter to return the top-left corner of the new window + * @return Boolean indication that the rectangle is a good place for the new window + */ static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos) { Window* const *wz; + /* Left part of the rectangle may be at most 1/4 off-screen, + * right part of the rectangle may be at most 1/2 off-screen + */ if (left < -(width>>2) || left > _screen.width - (width>>1)) return false; + /* Bottom part of the rectangle may be at most 1/4 off-screen */ if (top < 22 || top > _screen.height - (height>>2)) return false; /* Make sure it is not obscured by any window. */ @@ -854,13 +885,24 @@ return true; } +/** + * Find a good place for opening a new window of a given width and height. + * @param width Width of the new window + * @param height Height of the new window + * @return Top-left coordinate of the new window + */ static Point GetAutoPlacePosition(int width, int height) { Window* const *wz; Point pt; + /* First attempt, try top-left of the screen */ if (IsGoodAutoPlace1(0, 24, width, height, pt)) return pt; + /* Second attempt, try around all existing windows with a distance of 2 pixels. + * The new window must be entirely on-screen, and not overlap with an existing window. + * Eight starting points are tried, two at each corner. + */ FOR_ALL_WINDOWS(wz) { const Window *w = *wz; if (w->window_class == WC_MAIN_WINDOW) continue; @@ -875,6 +917,10 @@ if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt; } + /* Third attempt, try around all existing windows with a distance of 2 pixels. + * The new window may be partly off-screen, and must not overlap with an existing window. + * Only four starting points are tried. + */ FOR_ALL_WINDOWS(wz) { const Window *w = *wz; if (w->window_class == WC_MAIN_WINDOW) continue; @@ -885,6 +931,9 @@ if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt; } + /* Fourth and final attempt, put window at diagonal starting from (0, 24), try multiples + * of (+5, +5) + */ { int left = 0, top = 24; @@ -1021,6 +1070,7 @@ _last_z_window = _z_windows; _mouseover_last_w = NULL; _no_scroll = 0; + _scrolling_viewport = 0; } /** @@ -1217,11 +1267,11 @@ int nx = x; int ny = y; - if (_settings.gui.window_snap_radius != 0) { + if (_settings_client.gui.window_snap_radius != 0) { Window* const *vz; - int hsnap = _settings.gui.window_snap_radius; - int vsnap = _settings.gui.window_snap_radius; + int hsnap = _settings_client.gui.window_snap_radius; + int vsnap = _settings_client.gui.window_snap_radius; int delta; FOR_ALL_WINDOWS(vz) { @@ -1467,7 +1517,7 @@ static bool HandleViewportScroll() { - bool scrollwheel_scrolling = _settings.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); + bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); if (!_scrolling_viewport) return true; @@ -1487,7 +1537,7 @@ } Point delta; - if (_settings.gui.reverse_scroll) { + if (_settings_client.gui.reverse_scroll) { delta.x = -_cursor.delta.x; delta.y = -_cursor.delta.y; } else { @@ -1667,7 +1717,7 @@ return; } - if (_settings.gui.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) { + if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) { int x = _cursor.pos.x; int y = _cursor.pos.y; Window *w = FindWindowFromPt(x, y); @@ -1707,11 +1757,60 @@ extern void UpdateTileSelection(); extern bool VpHandlePlaceSizingDrag(); +static void ScrollMainViewport(int x, int y) +{ + if (_game_mode != GM_MENU) { + Window *w = FindWindowById(WC_MAIN_WINDOW, 0); + assert(w); + + w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom); + w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom); + } +} + +/** + * Describes all the different arrow key combinations the game allows + * when it is in scrolling mode. + * The real arrow keys are bitwise numbered as + * 1 = left + * 2 = up + * 4 = right + * 8 = down + */ +static const int8 scrollamt[16][2] = { + { 0, 0}, ///< no key specified + {-2, 0}, ///< 1 : left + { 0, -2}, ///< 2 : up + {-2, -1}, ///< 3 : left + up + { 2, 0}, ///< 4 : right + { 0, 0}, ///< 5 : left + right = nothing + { 2, -1}, ///< 6 : right + up + { 0, -2}, ///< 7 : right + left + up = up + { 0 ,2}, ///< 8 : down + {-2 ,1}, ///< 9 : down + left + { 0, 0}, ///< 10 : down + up = nothing + {-2, 0}, ///< 11 : left + up + down = left + { 2, 1}, ///< 12 : down + right + { 0, 2}, ///< 13 : left + right + down = down + { 2, 0}, ///< 14 : right + up + down = right + { 0, 0}, ///< 15 : left + up + right + down = nothing +}; + +static void HandleKeyScrolling() +{ + if (_dirkeys && !_no_scroll) { + int factor = _shift_pressed ? 50 : 10; + ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor); + } +} + void MouseLoop(MouseClick click, int mousewheel) { DecreaseWindowCounters(); HandlePlacePresize(); UpdateTileSelection(); + HandleKeyScrolling(); + if (!VpHandlePlaceSizingDrag()) return; if (!HandleDragDrop()) return; if (!HandleWindowDragging()) return; @@ -1719,7 +1818,7 @@ if (!HandleViewportScroll()) return; if (!HandleMouseOver()) return; - bool scrollwheel_scrolling = _settings.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); + 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; @@ -1734,7 +1833,7 @@ if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return; if (mousewheel != 0) { - if (_settings.gui.scrollwheel_scrolling == 0) { + if (_settings_client.gui.scrollwheel_scrolling == 0) { /* Send mousewheel event to window */ w->OnMouseWheel(mousewheel); } @@ -1979,6 +2078,12 @@ */ void CallWindowTickEvent() { + if (_scroller_click_timeout > 3) { + _scroller_click_timeout -= 3; + } else { + _scroller_click_timeout = 0; + } + for (Window * const *wz = _last_z_window; wz != _z_windows;) { (*--wz)->OnTick(); } @@ -2059,7 +2164,7 @@ w = FindWindowById(WC_MAIN_TOOLBAR, 0); } - switch (_settings.gui.toolbar_pos) { + switch (_settings_client.gui.toolbar_pos) { case 1: w->left = (_screen.width - w->width) / 2; break; case 2: w->left = _screen.width - w->width; break; default: w->left = 0; @@ -2068,6 +2173,30 @@ return w->left; } +void SetVScrollCount(Window *w, int num) +{ + w->vscroll.count = num; + num -= w->vscroll.cap; + if (num < 0) num = 0; + if (num < w->vscroll.pos) w->vscroll.pos = num; +} + +void SetVScroll2Count(Window *w, int num) +{ + w->vscroll2.count = num; + num -= w->vscroll2.cap; + if (num < 0) num = 0; + if (num < w->vscroll2.pos) w->vscroll2.pos = num; +} + +void SetHScrollCount(Window *w, int num) +{ + w->hscroll.count = num; + num -= w->hscroll.cap; + if (num < 0) num = 0; + if (num < w->hscroll.pos) w->hscroll.pos = num; +} + /** * Relocate all windows to fit the new size of the game application screen * @param neww New width of the game application screen