Index: src/settings.cpp =================================================================== --- src/settings.cpp (revision 16571) +++ src/settings.cpp (working copy) @@ -31,6 +31,7 @@ #include "train.h" #include "news_func.h" #include "window_func.h" +#include "window_gui.h" #include "strings_func.h" #include "vehicle_func.h" #include "sound_func.h" @@ -960,6 +961,20 @@ return true; } +static bool ShadeWindowModelChanged(int32 p1) +{ + Window *w; + /* If both scrolling and middle-clicking are disabled, unshade + * all shaded windows */ + if (_settings_client.gui.shade_on_scroll_on_caption == 0) { + FOR_ALL_WINDOWS_FROM_BACK(w) { + if (w->flags4 & WF_SHADE) UnShadeWindow(w); + } + } + + return true; +} + #ifdef ENABLE_NETWORK static bool UpdateClientName(int32 p1) Index: src/lang/english.txt =================================================================== --- src/lang/english.txt (revision 16571) +++ src/lang/english.txt (working copy) @@ -416,6 +416,7 @@ STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Close window STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Window title - drag this to move window STR_STICKY_BUTTON :{BLACK}Mark this window as uncloseable by the 'Close All Windows' key +STR_SHADE_BUTTON :{BLACK}Shade window - Only show the titlebar STR_RESIZE_BUTTON :{BLACK}Click and drag to resize this window STR_SAVELOAD_HOME_BUTTON :{BLACK}Click here to jump to the current default save/load directory STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC :{BLACK}Demolish buildings etc. on a square of land @@ -1017,6 +1018,11 @@ STR_CONFIG_SETTING_SCROLLWHEEL_OFF :Off STR_CONFIG_SETTING_SCROLLWHEEL_MULTIPLIER :{LTBLUE}Map scrollwheel speed: {ORANGE}{STRING1} +STR_CONFIG_SETTING_SHADE_ON_SCROLL_ON_CAPTION :{LTBLUE}Scrolling on window caption: {ORANGE}{STRING1} +STR_CONFIG_SETTING_SHADE_ON_SCROLL_ON_CAPTION_NONE :Off +STR_CONFIG_SETTING_SHADE_ON_SCROLL_ON_CAPTION_BUTTON :Shade window if it has a shade button +STR_CONFIG_SETTING_SHADE_ON_SCROLL_ON_CAPTION_ALL :Shade window + STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU :{LTBLUE}Right-click emulation: {ORANGE}{STRING1} STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_COMMAND :Command+Click STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_CONTROL :Ctrl+Click Index: src/settings_gui.cpp =================================================================== --- src/settings_gui.cpp (revision 16571) +++ src/settings_gui.cpp (working copy) @@ -1159,6 +1159,7 @@ * Since it's also able to completely disable the scrollwheel will we display it on all platforms anyway */ SettingEntry("gui.scrollwheel_scrolling"), SettingEntry("gui.scrollwheel_multiplier"), + SettingEntry("gui.shade_on_scroll_on_caption"), #ifdef __APPLE__ /* We might need to emulate a right mouse button on mac */ SettingEntry("gui.right_mouse_btn_emulation"), Index: src/table/sprites.h =================================================================== --- src/table/sprites.h (revision 16571) +++ src/table/sprites.h (working copy) @@ -49,7 +49,7 @@ /* Extra graphic spritenumbers */ SPR_OPENTTD_BASE = 4896, - OPENTTD_SPRITE_COUNT = 150, + OPENTTD_SPRITE_COUNT = 152, /* Halftile-selection sprites */ SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE, @@ -73,6 +73,8 @@ SPR_SHARED_ORDERS_ICON = SPR_OPENTTD_BASE + 50, SPR_PIN_UP = SPR_OPENTTD_BASE + 51, // pin icon SPR_PIN_DOWN = SPR_OPENTTD_BASE + 52, + SPR_SHADE_WINDOW = SPR_OPENTTD_BASE + 150, // shade icon + SPR_UNSHADE_WINDOW = SPR_OPENTTD_BASE + 151, SPR_CIRCLE_FOLDED = SPR_OPENTTD_BASE + 147, // (+) icon SPR_CIRCLE_UNFOLDED = SPR_OPENTTD_BASE + 148, // (-) icon Index: src/table/settings.h =================================================================== --- src/table/settings.h (revision 16571) +++ src/table/settings.h (working copy) @@ -24,6 +24,7 @@ static int32 CheckNoiseToleranceLevel(const char *value); static bool CheckFreeformEdges(int32 p1); static bool ChangeDynamicEngines(int32 p1); +static bool ShadeWindowModelChanged(int32 p1); #ifdef ENABLE_NETWORK static bool UpdateClientName(int32 p1); @@ -533,6 +534,7 @@ SDTC_BOOL(gui.prefer_teamchat, S, 0, false, STR_CONFIG_SETTING_PREFER_TEAMCHAT, NULL), SDTC_VAR(gui.scrollwheel_scrolling, SLE_UINT8, S, MS, 0, 0, 2, 0, STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING, NULL), SDTC_VAR(gui.scrollwheel_multiplier, SLE_UINT8, S, 0, 5, 1, 15, 1, STR_CONFIG_SETTING_SCROLLWHEEL_MULTIPLIER, NULL), + SDTC_VAR(gui.shade_on_scroll_on_caption,SLE_UINT8, S, MS, 1, 0, 2, 1, STR_CONFIG_SETTING_SHADE_ON_SCROLL_ON_CAPTION, ShadeWindowModelChanged), SDTC_BOOL(gui.pause_on_newgame, S, 0, false, STR_CONFIG_SETTING_PAUSE_ON_NEW_GAME, NULL), SDTC_VAR(gui.advanced_vehicle_list, SLE_UINT8, S, MS, 1, 0, 2, 0, STR_CONFIG_SETTING_ADVANCED_VEHICLE_LISTS, NULL), SDTC_BOOL(gui.timetable_in_ticks, S, 0, false, STR_CONFIG_SETTING_TIMETABLE_IN_TICKS, NULL), Index: src/window_gui.h =================================================================== --- src/window_gui.h (revision 16571) +++ src/window_gui.h (working copy) @@ -78,6 +78,7 @@ WDF_MODAL = 1 << 7, ///< The window is a modal child of some other window, meaning the parent is 'inactive' WDF_NO_FOCUS = 1 << 8, ///< This window won't get focus/make any other window lose focus when click + WDF_SHADE_BUTTON = 1 << 9, ///< Window has a shade button and can be shaded when scroll wheel is used on caption. }; /** @@ -162,6 +163,7 @@ int top; ///< y position of top edge of the window int width; ///< width of the window (number of pixels to the right in x direction) int height; ///< Height of the window (number of pixels down in y direction) + int height_shade; ///< Height of the window before shading Scrollbar hscroll; ///< Horizontal scroll bar Scrollbar vscroll; ///< First vertical scroll bar @@ -643,6 +645,7 @@ WF_WHITE_BORDER_ONE = 1 << 13, WF_WHITE_BORDER_MASK = 1 << 14 | WF_WHITE_BORDER_ONE, + WF_SHADE = 1 << 15, ///< Window is shaded by user }; Window *BringWindowToFrontById(WindowClass cls, WindowNumber number); @@ -708,4 +711,8 @@ void SetVScroll2Count(Window *w, int num); void SetHScrollCount(Window *w, int num); +void ShadeWindow(Window *w); +void UnShadeWindow(Window *w); +int GetWindowShadeHeight(const Window *w); + #endif /* WINDOW_GUI_H */ Index: src/widget.cpp =================================================================== --- src/widget.cpp (revision 16571) +++ src/widget.cpp (working copy) @@ -483,6 +483,26 @@ } /** + * Draw a shade box. + * @param r Rectangle of the box. + * @param colour Colour of the shade box. + * @param direction_up Shade direction (true for windows, false for news) + * @param clicked Box is lowered. + */ +static inline void DrawShadeBox(const Rect &r, Colours colour, bool direction_up, bool clicked) +{ + assert(r.right - r.left == 11); // To ensure the same sizes are used everywhere! + DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE); + if (direction_up) { + /* Windows shade up into caption/title bar */ + DrawSprite((clicked) ? SPR_SHADE_WINDOW : SPR_UNSHADE_WINDOW, PAL_NONE, r.left + 2 + clicked, r.top + 3 + clicked); + } else { + /* News windows shade down to status bar */ + DrawSprite((clicked) ? SPR_UNSHADE_WINDOW : SPR_SHADE_WINDOW, PAL_NONE, r.left + 2 + clicked, r.top + 3 + clicked); + } +} + +/** * Draw a sticky box. * @param r Rectangle of the box. * @param colour Colour of the sticky box. @@ -590,6 +610,11 @@ continue; } + /* If shaded, only draw the caption widgets. + * Drop every widget that starts lower than the caption. */ + int shade_height = GetWindowShadeHeight(this); + if ((this->flags4 & WF_SHADE) && r.top >= shade_height) continue; + switch (wi->type & WWT_MASK) { case WWT_IMGBTN: case WWT_IMGBTN_2: @@ -658,6 +683,11 @@ DrawStickyBox(r, wi->colour, !!(this->flags4 & WF_STICKY)); break; + case WWT_SHADEBOX: + assert(wi->data == 0); + DrawShadeBox(r, wi->colour, this->window_class != WC_NEWS_WINDOW, !!(this->flags4 & WF_SHADE)); + break; + case WWT_RESIZEBOX: assert(wi->data == 0); DrawResizeBox(r, wi->colour, wi->left < (this->width / 2), !!(this->flags4 & WF_SIZING)); @@ -1752,6 +1782,11 @@ const DrawPixelInfo *dpi = _cur_dpi; if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return; + /* If shaded, only draw the caption widgets. + * Drop every widget that starts lower than the caption. */ + int shade_height = GetWindowShadeHeight(w); + if ((w->flags4 & WF_SHADE) && r.top >= shade_height) return; + switch (this->type) { case WWT_PANEL: assert(this->widget_data == 0); @@ -1870,6 +1905,12 @@ this->SetDataTip(STR_NULL, STR_STICKY_BUTTON); break; + case WWT_SHADEBOX: + this->SetFill(false, false); + this->SetMinimalSize(12, 14); + this->SetDataTip(STR_NULL, STR_SHADE_BUTTON); + break; + case WWT_RESIZEBOX: this->SetFill(false, false); this->SetMinimalSize(12, 12); @@ -1905,7 +1946,13 @@ const DrawPixelInfo *dpi = _cur_dpi; if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return; + /* If shaded, only draw the caption widgets. + * Drop every widget that starts lower than the caption. */ + int shade_height = GetWindowShadeHeight(w); + if ((w->flags4 & WF_SHADE) && r.top >= shade_height) return; + bool clicked = this->IsLowered(); + switch (this->type) { case WWT_EMPTY: break; @@ -1974,6 +2021,11 @@ DrawStickyBox(r, this->colour, !!(w->flags4 & WF_STICKY)); break; + case WWT_SHADEBOX: + assert(this->widget_data == 0); + DrawShadeBox(r, this->colour, w->window_class != WC_NEWS_WINDOW, !!(w->flags4 & WF_SHADE)); + break; + case WWT_RESIZEBOX: assert(this->widget_data == 0); DrawResizeBox(r, this->colour, this->pos_x < (uint)(w->width / 2), !!(w->flags4 & WF_SIZING)); Index: src/widget_type.h =================================================================== --- src/widget_type.h (revision 16571) +++ src/widget_type.h (working copy) @@ -87,6 +87,7 @@ WWT_CAPTION, ///< Window caption (window title between closebox and stickybox) WWT_HSCROLLBAR, ///< Horizontal scrollbar + WWT_SHADEBOX, ///< Shade box (at top-right of a window, between caption and stickybox) WWT_STICKYBOX, ///< Sticky box (normally at top-right of a window) WWT_SCROLL2BAR, ///< 2nd vertical scrollbar WWT_RESIZEBOX, ///< Resize box (normally at bottom-right of a window) Index: src/settings_type.h =================================================================== --- src/settings_type.h (revision 16571) +++ src/settings_type.h (working copy) @@ -68,6 +68,7 @@ uint8 right_mouse_btn_emulation; ///< should we emulate right mouse clicking? uint8 scrollwheel_scrolling; ///< scrolling using the scroll wheel? uint8 scrollwheel_multiplier; ///< how much 'wheel' per incoming event from the OS? + uint8 shade_on_scroll_on_caption; ///< shade window when scrolling on window caption: 0=never, 1=windows with shadebutton, 2=all windows bool left_mouse_btn_scrolling; ///< left mouse button scroll bool pause_on_newgame; ///< whether to start new games paused or not bool enable_signal_gui; ///< show the signal GUI when the signal button is pressed Index: src/window.cpp =================================================================== --- src/window.cpp (revision 16571) +++ src/window.cpp (working copy) @@ -374,6 +374,16 @@ w->InvalidateWidget(widget_index); return; } + + if (widget_type == WWT_SHADEBOX) { + w->InvalidateWidget(widget_index); + if (w->flags4 & WF_SHADE) { + UnShadeWindow(w); + } else { + ShadeWindow(w); + } + return; + } } Point pt = { x, y }; @@ -422,7 +432,8 @@ /** * Dispatch the mousewheel-action to the window. - * The window will scroll any compatible scrollbars if the mouse is pointed over the bar or its contents + * The window will scroll any compatible scrollbars if the mouse is pointed over the bar or its contents. + * The window will shade/unshade if the mouse is pointed over the caption. * @param w Window * @param widget the widget where the scrollwheel was used * @param wheel scroll up or down @@ -432,6 +443,8 @@ if (widget < 0) return; Scrollbar *sb = NULL; + bool shade_event = false; + if (w->widget != NULL) { const Widget *wi1 = &w->widget[widget]; const Widget *wi2 = &w->widget[widget + 1]; @@ -440,6 +453,20 @@ } else if (wi1->type == WWT_SCROLL2BAR || wi2->type == WWT_SCROLL2BAR) { sb = &w->vscroll2; } + + /* Scroll on window caption or on news window: shade/unshade. + * This one is for the old style non-nested-widget-windows */ + if ((w->window_class == WC_NEWS_WINDOW) || + (wi1->type == WWT_CAPTION)) { + shade_event = true; + } + } else { + /* Scroll on window caption or on news window: shade/unshade. + * This one is for the new style nested-widget-windows */ + if ((w->window_class == WC_NEWS_WINDOW) || + (w->nested_array[widget]->type == WWT_CAPTION)) { + shade_event = true; + } } if (w->nested_array != NULL && (uint)widget < w->nested_array_size) sb = w->nested_array[widget]->FindScrollbar(w); @@ -451,6 +478,19 @@ w->SetDirty(); } } + + /* Scroll on window caption or on news window: shade/unshade. + * Only shade if settings allow shading */ + if ((shade_event) && + (_settings_client.gui.shade_on_scroll_on_caption != 0) && + ((_settings_client.gui.shade_on_scroll_on_caption == 2) || (w->desc_flags & WDF_SHADE_BUTTON))) { + if (w->flags4 & WF_SHADE) { + UnShadeWindow(w); + } + else if (!(w->flags4 & WF_SHADE)) { + ShadeWindow(w); + } + } } /** @@ -724,6 +764,7 @@ /** Find a window and make it the top-window on the screen. The window * gets a white border for a brief period of time to visualize its "activation" + * If the window was shaded, it will be unshaded. * @param cls WindowClass of the window to activate * @param number WindowNumber of the window to activate * @return a pointer to the window thus activated */ @@ -734,6 +775,12 @@ if (w != NULL) { w->flags4 |= WF_WHITE_BORDER_MASK; BringWindowToFront(w); + + /* Restore original window height if window was shaded */ + if (w->flags4 & WF_SHADE) { + UnShadeWindow(w); + } + w->SetDirty(); } @@ -794,6 +841,119 @@ w->SetDirty(); } +int GetWindowShadeHeight(const Window *w) +{ + /* Shaded window is only as high as title bar. + * Find height of title bar via CAPTION widget, or if that + * doesn't exist, fall back on hardcoded value. + * News windows don't have a CAPTION widget. */ + if (w->widget != NULL) { + const Widget *caption = w->GetWidgetOfType(WWT_CAPTION); + if (caption != NULL) { + return caption->bottom - caption->top + 1; + } else { + return 14; + } + } else { + assert(w->nested_root != NULL); + const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION); + if (caption != NULL) { + return caption->current_y; + } else { + return 14; + } + } +} + +/** Shade the window: only draw the title bar and hide the rest of the window. + * The unshaded window is marked dirty for a repaint. + * @param w window that is shaded + * @return pointer to the window, the same as the input pointer + */ +void ShadeWindow(Window *w) +{ + w->flags4 |= WF_SHADE; + + /* Save original window height */ + w->height_shade = w->height; + + /* Set shade height for window */ + w->height = GetWindowShadeHeight(w); + + /* Mark original window dirty */ + SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height_shade); +} + +/** Unshade the window: draw the whole window. + * The window is marked dirty for a repaint. + * @param w window that is shaded + * @return pointer to the window, the same as the input pointer + */ +void UnShadeWindow(Window *w) +{ + int new_top = w->top; + /* Remember shaded size so it can be marked dirty */ + int top_orig = w->top; + int height_orig = w->height; + + w->flags4 &= ~WF_SHADE; + + /* Restore original window height */ + w->height = w->height_shade; + + /* Move window up if bottom gets below screen bottom */ + /* or gets behind status bar or chat window */ + if ((w->top + w->height_shade) > _screen.height) { + new_top = _screen.height - w->height_shade; + } + + const Window *ws = FindWindowById(WC_STATUS_BAR, 0); + if (ws != NULL) { + /* Make sure the status bar doesn't overlap the window */ + if (((new_top + w->height_shade) > ws->top) && + (w->left < (ws->left + ws->width)) && + ((w->left + w->width) > ws->left)) { + new_top = ws->top - w->height_shade; + } + } + + const Window *wc = FindWindowById(WC_SEND_NETWORK_MSG, 0); + if (wc != NULL) { + /* Make sure chat window doesn't overlap the window */ + if (((new_top + w->height_shade) > wc->top) && + (w->left < (wc->left + wc->width)) && + ((w->left + w->width) > wc->left)) { + new_top = wc->top - w->height_shade; + } + } + + /* Don't move the window off the screen */ + if (new_top < 0) new_top = 0; + + /* Make sure the main tool bar doesn't overlap the window */ + const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); + if (wt != NULL) { + if ((wt->height > new_top) && + (w->left < (wt->left + wt->width)) && + ((w->left + w->width) > wt->left)) { + new_top = wt->height; + } + } + + /* If window contains a viewport, move that too */ + if (w->viewport != NULL) { + w->viewport->top += new_top - w->top; + } + + /* Move window to new position */ + w->top = new_top; + + /* Mark the original location of the shaded window dirty. */ + SetDirtyBlocks(w->left, top_orig, w->left + w->width, top_orig + height_orig); + + w->SetDirty(); +} + /** * Assign widgets to a new window by initialising its widget pointers, and by * copying the widget array \a widget to \c w->widget to allow for resizable @@ -1881,9 +2041,10 @@ } /* Window sizes don't interfere, leave z-order alone */ + /* Account for possible unshaded size */ if (w->left + w->width <= u->left || u->left + u->width <= w->left || - w->top + w->height <= u->top || + w->top + max(w->height, w->height_shade) <= u->top || u->top + u->height <= w->top) { continue; } @@ -2304,7 +2465,8 @@ DrawDirtyBlocks(); FOR_ALL_WINDOWS_FROM_BACK(w) { - if (w->viewport != NULL) UpdateViewportPosition(w); + /* Update viewport only if window isn't shaded */ + if ((w->viewport != NULL) && !(w->flags4 & WF_SHADE)) UpdateViewportPosition(w); } NetworkDrawChatMessage(); /* Redraw mouse cursor in case it was hidden */ Index: src/gfxinit.cpp =================================================================== --- src/gfxinit.cpp (revision 16571) +++ src/gfxinit.cpp (working copy) @@ -271,6 +271,9 @@ _palette_remap_grf[i] = (_use_palette != _used_graphics_set->palette); LoadGrfFile(_used_graphics_set->files[GFT_LOGOS].filename, 4793, i++); + /* Load sprites for shade and unshade buttons */ + LoadGrfFile("shade.grf", SPR_SHADE_WINDOW, i++); + /* * Load additional sprites for climates other than temperate. * This overwrites some of the temperate sprites, such as foundations