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) { |
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); |
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 |