Change how dirty screen, window and viewport areas are tracked for later redrawing

Track dirty viewport areas seperately form general screen redraws.
Maintain a dirty block grid per viewport, with a smaller block size.
Use even smaller block size in viewport map mode.

Use a rectangle array for general screen redraws instead of a block grid.

Add a dirty bit to windows and widgets, to simplify the common case
of repainting a whole window or widget, without catching
neighbouring windows or viewports.
This commit is contained in:
Jonathan G Rennison
2020-02-21 19:45:07 +00:00
parent d740a19e5f
commit 8f442500ea
18 changed files with 626 additions and 182 deletions

View File

@@ -20,6 +20,10 @@
#include "window_func.h" #include "window_func.h"
#include "newgrf_debug.h" #include "newgrf_debug.h"
#include "thread.h" #include "thread.h"
#include "widget_type.h"
#include "window_gui.h"
#include "framerate_type.h"
#include "transparency.h"
#include "table/palettes.h" #include "table/palettes.h"
#include "table/string_colours.h" #include "table/string_colours.h"
@@ -68,17 +72,16 @@ ZoomLevel _font_zoom; ///< Font Zoom level
* *
* @ingroup dirty * @ingroup dirty
*/ */
static Rect _invalid_rect;
static const byte *_colour_remap_ptr; static const byte *_colour_remap_ptr;
static byte _string_colourremap[3]; ///< Recoloursprite for stringdrawing. The grf loader ensures that #ST_FONT sprites only use colours 0 to 2. static byte _string_colourremap[3]; ///< Recoloursprite for stringdrawing. The grf loader ensures that #ST_FONT sprites only use colours 0 to 2.
static const uint DIRTY_BLOCK_HEIGHT = 8; static const uint DIRTY_BLOCK_HEIGHT = 8;
static const uint DIRTY_BLOCK_WIDTH = 64; static const uint DIRTY_BLOCK_WIDTH = 64;
static uint _dirty_bytes_per_line = 0;
static byte *_dirty_blocks = nullptr;
extern uint _dirty_block_colour; extern uint _dirty_block_colour;
static bool _whole_screen_dirty = false;
static std::vector<Rect> _dirty_blocks;
/** /**
* Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen. * Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
@@ -1317,16 +1320,11 @@ void GetBroadestDigit(uint *front, uint *next, FontSize size)
void ScreenSizeChanged() void ScreenSizeChanged()
{ {
_dirty_bytes_per_line = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH); MarkWholeScreenDirty();
_dirty_blocks = ReallocT<byte>(_dirty_blocks, _dirty_bytes_per_line * CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT));
extern uint32 *_vp_map_line; extern uint32 *_vp_map_line;
_vp_map_line = ReallocT<uint32>(_vp_map_line, _screen.width); _vp_map_line = ReallocT<uint32>(_vp_map_line, _screen.width);
/* check the dirty rect */
if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
/* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */ /* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
_cursor.visible = false; _cursor.visible = false;
} }
@@ -1424,6 +1422,62 @@ void RedrawScreenRect(int left, int top, int right, int bottom)
VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top); VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top);
} }
static std::vector<Rect> _dirty_viewport_occlusions;
static const ViewPort *_dirty_viewport;
static NWidgetDisplay _dirty_viewport_disp_flags;
static void DrawDirtyViewport(uint occlusion, int left, int top, int right, int bottom)
{
for(; occlusion < _dirty_viewport_occlusions.size(); occlusion++) {
const Rect &occ = _dirty_viewport_occlusions[occlusion];
if (right > occ.left &&
bottom > occ.top &&
left < occ.right &&
top < occ.bottom) {
/* occlusion and draw rectangle intersect with each other */
int x;
if (left < (x = occ.left)) {
DrawDirtyViewport(occlusion + 1, left, top, x, bottom);
DrawDirtyViewport(occlusion, x, top, right, bottom);
return;
}
if (right > (x = occ.right)) {
DrawDirtyViewport(occlusion, left, top, x, bottom);
DrawDirtyViewport(occlusion + 1, x, top, right, bottom);
return;
}
if (top < (x = occ.top)) {
DrawDirtyViewport(occlusion + 1, left, top, right, x);
DrawDirtyViewport(occlusion, left, x, right, bottom);
return;
}
if (bottom > (x = occ.bottom)) {
DrawDirtyViewport(occlusion, left, top, right, x);
DrawDirtyViewport(occlusion + 1, left, x, right, bottom);
return;
}
return;
}
}
if (_game_mode == GM_MENU) {
RedrawScreenRect(left, top, right, bottom);
} else {
extern void ViewportDrawChk(ViewPort *vp, int left, int top, int right, int bottom);
ViewportDrawChk(_dirty_viewport, left, top, right, bottom);
if (_dirty_viewport_disp_flags & (ND_SHADE_GREY | ND_SHADE_DIMMED)) {
GfxFillRect(left, top, right, bottom,
(_dirty_viewport_disp_flags & ND_SHADE_DIMMED) ? PALETTE_TO_TRANSPARENT : PALETTE_NEWSPAPER, FILLRECT_RECOLOUR);
}
VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top);
}
}
/** /**
* Repaints the rectangle blocks which are marked as 'dirty'. * Repaints the rectangle blocks which are marked as 'dirty'.
* *
@@ -1431,11 +1485,7 @@ void RedrawScreenRect(int left, int top, int right, int bottom)
*/ */
void DrawDirtyBlocks() void DrawDirtyBlocks()
{ {
byte *b = _dirty_blocks; static std::vector<NWidgetBase *> dirty_widgets;
const int w = Align(_screen.width, DIRTY_BLOCK_WIDTH);
const int h = Align(_screen.height, DIRTY_BLOCK_HEIGHT);
int x;
int y;
if (HasModalProgress()) { if (HasModalProgress()) {
/* We are generating the world, so release our rights to the map and /* We are generating the world, so release our rights to the map and
@@ -1460,79 +1510,285 @@ void DrawDirtyBlocks()
if (_switch_mode != SM_NONE && !HasModalProgress()) return; if (_switch_mode != SM_NONE && !HasModalProgress()) return;
} }
y = 0; if (_whole_screen_dirty) {
RedrawScreenRect(0, 0, _screen.width, _screen.height);
Window *w;
FOR_ALL_WINDOWS_FROM_BACK(w) {
w->flags &= ~(WF_DIRTY | WF_WIDGETS_DIRTY);
}
_whole_screen_dirty = false;
} else {
bool cleared_overlays = false;
auto clear_overlays = [&]() {
if (cleared_overlays) return;
if (_cursor.visible) UndrawMouseCursor();
if (_networking) NetworkUndrawChatMessage();
cleared_overlays = true;
};
DrawPixelInfo *old_dpi = _cur_dpi;
DrawPixelInfo bk;
_cur_dpi = &bk;
extern void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom, bool gfx_dirty);
Window *w;
FOR_ALL_WINDOWS_FROM_BACK(w) {
if (!MayBeShown(w)) continue;
if (w->viewport != nullptr) w->viewport->is_drawn = false;
if (w->flags & WF_DIRTY) {
clear_overlays();
DrawOverlappedWindow(w, max(0, w->left), max(0, w->top), min(_screen.width, w->left + w->width), min(_screen.height, w->top + w->height), true);
w->flags &= ~(WF_DIRTY | WF_WIDGETS_DIRTY);
} else if (w->flags & WF_WIDGETS_DIRTY) {
if (w->nested_root != nullptr) {
clear_overlays();
w->nested_root->FillDirtyWidgets(dirty_widgets);
for (NWidgetBase *widget : dirty_widgets) {
DrawOverlappedWindow(w, max(0, w->left + widget->pos_x), max(0, w->top + widget->pos_y), min(_screen.width, w->left + widget->pos_x + widget->current_x), min(_screen.height, w->top + widget->pos_y + widget->current_y), true);
}
dirty_widgets.clear();
}
w->flags &= ~WF_WIDGETS_DIRTY;
}
if (w->viewport != nullptr) {
ViewPort *vp = w->viewport;
if (vp->is_drawn) {
vp->ClearDirty();
} else if (vp->is_dirty) {
clear_overlays();
PerformanceAccumulator framerate(PFE_DRAWWORLD);
_cur_dpi->left = 0;
_cur_dpi->top = 0;
_cur_dpi->width = _screen.width;
_cur_dpi->height = _screen.height;
_cur_dpi->pitch = _screen.pitch;
_cur_dpi->dst_ptr = _screen.dst_ptr;
_cur_dpi->zoom = ZOOM_LVL_NORMAL;
_dirty_viewport = vp;
_dirty_viewport_disp_flags = w->viewport_widget->disp_flags;
TransparencyOptionBits to_backup = _transparency_opt;
if (_dirty_viewport_disp_flags & ND_NO_TRANSPARENCY) {
_transparency_opt &= (1 << TO_SIGNS) | (1 << TO_LOADING); // Disable all transparency, except textual stuff
}
{
int left = vp->left;
int top = vp->top;
int right = vp->left + vp->width;
int bottom = vp->top + vp->height;
_dirty_viewport_occlusions.clear();
const Window *v;
FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
if (MayBeShown(v) &&
right > v->left &&
bottom > v->top &&
left < v->left + v->width &&
top < v->top + v->height) {
_dirty_viewport_occlusions.push_back({ v->left, v->top, v->left + v->width, v->top + v->height });
}
}
for (const Rect &r : _dirty_blocks) {
if (right > r.left &&
bottom > r.top &&
left < r.right &&
top < r.bottom) {
_dirty_viewport_occlusions.push_back({ r.left, r.top, r.right, r.bottom });
}
}
}
const uint grid_w = vp->dirty_blocks_per_row;
const uint grid_h = vp->dirty_blocks_per_column;
uint pos = 0;
uint x = 0;
do { do {
x = 0; uint y = 0;
do { do {
if (*b != 0) { if (vp->dirty_blocks[pos]) {
int left; uint left = x;
int top; uint top = y;
int right = x + DIRTY_BLOCK_WIDTH; uint right = x + 1;
int bottom = y; uint bottom = y;
byte *p = b; uint p = pos;
int h2;
/* First try coalescing downwards */ /* First try coalescing downwards */
do { do {
*p = 0; vp->dirty_blocks[p] = false;
p += _dirty_bytes_per_line; p++;
bottom += DIRTY_BLOCK_HEIGHT; bottom++;
} while (bottom != h && *p != 0); } while (bottom != grid_h && vp->dirty_blocks[p]);
/* Try coalescing to the right too. */ /* Try coalescing to the right too. */
h2 = (bottom - y) / DIRTY_BLOCK_HEIGHT; uint block_h = (bottom - y);
assert(h2 > 0); p = pos;
p = b;
while (right != w) { while (right != grid_w) {
byte *p2 = ++p; uint p2 = (p += grid_h);
int h = h2; uint check_h = block_h;
/* Check if a full line of dirty flags is set. */ /* Check if a full line of dirty flags is set. */
do { do {
if (!*p2) goto no_more_coalesc; if (!vp->dirty_blocks[p2]) goto no_more_coalesc;
p2 += _dirty_bytes_per_line; p2++;
} while (--h != 0); } while (--check_h != 0);
/* Wohoo, can combine it one step to the right! /* Wohoo, can combine it one step to the right!
* Do that, and clear the bits. */ * Do that, and clear the bits. */
right += DIRTY_BLOCK_WIDTH; right++;
h = h2; check_h = block_h;
p2 = p; p2 = p;
do { do {
*p2 = 0; vp->dirty_blocks[p2] = false;
p2 += _dirty_bytes_per_line; p2++;
} while (--h != 0); } while (--check_h != 0);
} }
no_more_coalesc: no_more_coalesc:
left = x; assert(_cur_dpi == &bk);
top = y; int draw_left = max<int>(0, ((left == 0) ? 0 : vp->dirty_block_left_margin + (left << vp->GetDirtyBlockWidthShift())) + vp->left);
int draw_top = max<int>(0, (top << vp->GetDirtyBlockHeightShift()) + vp->top);
int draw_right = min<int>(_screen.width, min<int>((right << vp->GetDirtyBlockWidthShift()) + vp->dirty_block_left_margin, vp->width) + vp->left);
int draw_bottom = min<int>(_screen.height, min<int>(bottom << vp->GetDirtyBlockHeightShift(), vp->height) + vp->top);
if (draw_left < draw_right && draw_top < draw_bottom) {
DrawDirtyViewport(0, draw_left, draw_top, draw_right, draw_bottom);
}
}
} while (pos++, ++y != grid_h);
} while (++x != grid_w);
if (left < _invalid_rect.left ) left = _invalid_rect.left; _transparency_opt = to_backup;
if (top < _invalid_rect.top ) top = _invalid_rect.top; w->viewport->ClearDirty();
if (right > _invalid_rect.right ) right = _invalid_rect.right; }
if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom; }
if (left < right && top < bottom) {
RedrawScreenRect(left, top, right, bottom);
} }
} _cur_dpi = old_dpi;
} while (b++, (x += DIRTY_BLOCK_WIDTH) != w);
} while (b += -(int)(w / DIRTY_BLOCK_WIDTH) + _dirty_bytes_per_line, (y += DIRTY_BLOCK_HEIGHT) != h);
for (const Rect &r : _dirty_blocks) {
RedrawScreenRect(r.left, r.top, r.right, r.bottom);
}
}
_dirty_blocks.clear();
++_dirty_block_colour; ++_dirty_block_colour;
_invalid_rect.left = w; }
_invalid_rect.top = h;
_invalid_rect.right = 0; void UnsetDirtyBlocks(int left, int top, int right, int bottom)
_invalid_rect.bottom = 0; {
if (_whole_screen_dirty) return;
for (uint i = 0; i < _dirty_blocks.size(); i++) {
Rect &r = _dirty_blocks[i];
if (left < r.right &&
right > r.left &&
top < r.bottom &&
bottom > r.top) {
/* overlap of some sort */
if (left <= r.left &&
right >= r.right &&
top <= r.top &&
bottom >= r.bottom) {
/* dirty rect entirely in subtraction area */
r = _dirty_blocks.back();
_dirty_blocks.pop_back();
i--;
continue;
}
if (r.left < left) {
Rect n = { left, r.top, r.right, r.bottom };
r.right = left;
_dirty_blocks.push_back(n);
continue;
}
if (r.right > right) {
Rect n = { r.left, r.top, right, r.bottom };
r.left = right;
_dirty_blocks.push_back(n);
continue;
}
if (r.top < top) {
Rect n = { r.left, top, r.right, r.bottom };
r.bottom = top;
_dirty_blocks.push_back(n);
continue;
}
if (r.bottom > bottom) {
Rect n = { r.left, r.top, r.right, bottom };
r.top = bottom;
_dirty_blocks.push_back(n);
continue;
}
}
}
}
static void AddDirtyBlocks(uint start, int left, int top, int right, int bottom)
{
if (bottom <= top || right <= left) return;
for (; start < _dirty_blocks.size(); start++) {
Rect &r = _dirty_blocks[start];
if (left <= r.right &&
right >= r.left &&
top <= r.bottom &&
bottom >= r.top) {
/* overlap or contact of some sort */
if (left >= r.left &&
right <= r.right &&
top >= r.top &&
bottom <= r.bottom) {
/* entirely contained by existing */
return;
}
if (left <= r.left &&
right >= r.right &&
top <= r.top &&
bottom >= r.bottom) {
/* entirely contains existing */
r = _dirty_blocks.back();
_dirty_blocks.pop_back();
start--;
continue;
}
if (left < r.left && right > r.left) {
int middle = r.left;
AddDirtyBlocks(start, left, top, middle, bottom);
AddDirtyBlocks(start, middle, top, right, bottom);
return;
}
if (right > r.right && left < r.right) {
int middle = r.right;
AddDirtyBlocks(start, left, top, middle, bottom);
AddDirtyBlocks(start, middle, top, right, bottom);
return;
}
if (top < r.top && bottom > r.top) {
int middle = r.top;
AddDirtyBlocks(start, left, top, right, middle);
AddDirtyBlocks(start, left, middle, right, bottom);
return;
}
if (bottom > r.bottom && top < r.bottom) {
int middle = r.bottom;
AddDirtyBlocks(start, left, top, right, middle);
AddDirtyBlocks(start, left, middle, right, bottom);
return;
}
}
}
_dirty_blocks.push_back({ left, top, right, bottom });
} }
/** /**
* This function extends the internal _invalid_rect rectangle as it * Note the point (0,0) is top left.
* now contains the rectangle defined by the given parameters. Note
* the point (0,0) is top left.
* *
* @param left The left edge of the rectangle * @param left The left edge of the rectangle
* @param top The top edge of the rectangle * @param top The top edge of the rectangle
@@ -1546,39 +1802,14 @@ void DrawDirtyBlocks()
*/ */
void SetDirtyBlocks(int left, int top, int right, int bottom) void SetDirtyBlocks(int left, int top, int right, int bottom)
{ {
byte *b; if (_whole_screen_dirty) return;
int width;
int height;
if (left < 0) left = 0; if (left < 0) left = 0;
if (top < 0) top = 0; if (top < 0) top = 0;
if (right > _screen.width) right = _screen.width; if (right > _screen.width) right = _screen.width;
if (bottom > _screen.height) bottom = _screen.height; if (bottom > _screen.height) bottom = _screen.height;
if (left >= right || top >= bottom) return; AddDirtyBlocks(0, left, top, right, bottom);
if (left < _invalid_rect.left ) _invalid_rect.left = left;
if (top < _invalid_rect.top ) _invalid_rect.top = top;
if (right > _invalid_rect.right ) _invalid_rect.right = right;
if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
left /= DIRTY_BLOCK_WIDTH;
top /= DIRTY_BLOCK_HEIGHT;
b = _dirty_blocks + top * _dirty_bytes_per_line + left;
width = ((right - 1) / DIRTY_BLOCK_WIDTH) - left + 1;
height = ((bottom - 1) / DIRTY_BLOCK_HEIGHT) - top + 1;
assert(width > 0 && height > 0);
do {
int i = width;
do b[--i] = 0xFF; while (i != 0);
b += _dirty_bytes_per_line;
} while (--height != 0);
} }
/** /**
@@ -1589,7 +1820,7 @@ void SetDirtyBlocks(int left, int top, int right, int bottom)
*/ */
void MarkWholeScreenDirty() void MarkWholeScreenDirty()
{ {
SetDirtyBlocks(0, 0, _screen.width, _screen.height); _whole_screen_dirty = true;
} }
/** /**

View File

@@ -21,7 +21,7 @@
* (this is btw. also possible if needed). This is used to avoid a * (this is btw. also possible if needed). This is used to avoid a
* flickering of the screen by the video driver constantly repainting it. * flickering of the screen by the video driver constantly repainting it.
* *
* This whole mechanism is controlled by an rectangle defined in #_invalid_rect. This * This whole mechanism was controlled by an rectangle defined in #_invalid_rect. This
* rectangle defines the area on the screen which must be repaint. If a new object * rectangle defines the area on the screen which must be repaint. If a new object
* needs to be repainted this rectangle is extended to 'catch' the object on the * needs to be repainted this rectangle is extended to 'catch' the object on the
* screen. At some point (which is normally uninteresting for patch writers) this * screen. At some point (which is normally uninteresting for patch writers) this
@@ -32,7 +32,6 @@
* rectangle information. Then a new round begins by marking objects "dirty". * rectangle information. Then a new round begins by marking objects "dirty".
* *
* @see VideoDriver::MakeDirty * @see VideoDriver::MakeDirty
* @see _invalid_rect
* @see _screen * @see _screen
*/ */
@@ -134,6 +133,7 @@ const char *GetCharAtPosition(const char *str, int x, FontSize start_fontsize =
void DrawDirtyBlocks(); void DrawDirtyBlocks();
void SetDirtyBlocks(int left, int top, int right, int bottom); void SetDirtyBlocks(int left, int top, int right, int bottom);
void UnsetDirtyBlocks(int left, int top, int right, int bottom);
void MarkWholeScreenDirty(); void MarkWholeScreenDirty();
void GfxInitPalettes(); void GfxInitPalettes();

View File

@@ -63,7 +63,7 @@ public:
* @param company_mask Bitmask of companies to be shown. * @param company_mask Bitmask of companies to be shown.
* @param scale Desired thickness of lines and size of station dots. * @param scale Desired thickness of lines and size of station dots.
*/ */
LinkGraphOverlay(const Window *w, uint wid, CargoTypes cargo_mask, uint32 company_mask, uint scale) : LinkGraphOverlay(Window *w, uint wid, CargoTypes cargo_mask, uint32 company_mask, uint scale) :
window(w), widget_id(wid), cargo_mask(cargo_mask), company_mask(company_mask), scale(scale) window(w), widget_id(wid), cargo_mask(cargo_mask), company_mask(company_mask), scale(scale)
{} {}
@@ -83,7 +83,7 @@ public:
uint32 GetCompanyMask() { return this->company_mask; } uint32 GetCompanyMask() { return this->company_mask; }
protected: protected:
const Window *window; ///< Window to be drawn into. Window *window; ///< Window to be drawn into.
const uint widget_id; ///< ID of Widget in Window to be drawn to. const uint widget_id; ///< ID of Widget in Window to be drawn to.
CargoTypes cargo_mask; ///< Bitmask of cargos to be displayed. CargoTypes cargo_mask; ///< Bitmask of cargos to be displayed.
uint32 company_mask; ///< Bitmask of companies to be displayed. uint32 company_mask; ///< Bitmask of companies to be displayed.

View File

@@ -149,6 +149,7 @@ bool DoZoomInOutWindow(ZoomStateChange how, Window *w)
if (vp != nullptr) { // the vp can be null when how == ZOOM_NONE if (vp != nullptr) { // the vp can be null when how == ZOOM_NONE
vp->virtual_left = w->viewport->scrollpos_x; vp->virtual_left = w->viewport->scrollpos_x;
vp->virtual_top = w->viewport->scrollpos_y; vp->virtual_top = w->viewport->scrollpos_y;
UpdateViewportSizeZoom(vp);
} }
/* Update the windows that have zoom-buttons to perhaps disable their buttons */ /* Update the windows that have zoom-buttons to perhaps disable their buttons */
w->InvalidateData(); w->InvalidateData();
@@ -183,6 +184,7 @@ void FixTitleGameZoom()
vp->zoom = _gui_zoom; vp->zoom = _gui_zoom;
vp->virtual_width = ScaleByZoom(vp->width, vp->zoom); vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
vp->virtual_height = ScaleByZoom(vp->height, vp->zoom); vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
UpdateViewportSizeZoom(vp);
} }
static const struct NWidgetPart _nested_main_window_widgets[] = { static const struct NWidgetPart _nested_main_window_widgets[] = {
@@ -613,5 +615,4 @@ void GameSizeChanged()
_cur_resolution.height = _screen.height; _cur_resolution.height = _screen.height;
ScreenSizeChanged(); ScreenSizeChanged();
RelocateAllWindows(_screen.width, _screen.height); RelocateAllWindows(_screen.width, _screen.height);
MarkWholeScreenDirty();
} }

View File

@@ -206,6 +206,19 @@ public:
return nullptr; return nullptr;
} }
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override
{
if (this->base_flags & WBF_DIRTY) {
dirty_widgets.push_back(this);
} else {
int i = 0;
for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) {
if (!this->visible[i++]) continue;
child_wid->FillDirtyWidgets(dirty_widgets);
}
}
}
/** /**
* Checks whether the given widget is actually visible. * Checks whether the given widget is actually visible.
* @param widget the widget to check for visibility * @param widget the widget to check for visibility

View File

@@ -1781,6 +1781,17 @@ public:
this->acs->Draw(w); this->acs->Draw(w);
this->inf->Draw(w); this->inf->Draw(w);
} }
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override
{
if (this->base_flags & WBF_DIRTY) {
dirty_widgets.push_back(this);
} else {
if (this->editable) this->avs->FillDirtyWidgets(dirty_widgets);
this->acs->FillDirtyWidgets(dirty_widgets);
this->inf->FillDirtyWidgets(dirty_widgets);
}
}
}; };
const uint NWidgetNewGRFDisplay::INTER_LIST_SPACING = WD_RESIZEBOX_WIDTH + 1; const uint NWidgetNewGRFDisplay::INTER_LIST_SPACING = WD_RESIZEBOX_WIDTH + 1;

View File

@@ -789,6 +789,7 @@ void SetupScreenshotViewport(ScreenshotType t, ViewPort *vp)
break; break;
} }
} }
UpdateViewportSizeZoom(vp);
} }
/** /**

View File

@@ -1735,6 +1735,15 @@ public:
{ {
for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) child_wid->Draw(w); for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) child_wid->Draw(w);
} }
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override
{
if (this->base_flags & WBF_DIRTY) {
dirty_widgets.push_back(this);
} else {
for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) child_wid->FillDirtyWidgets(dirty_widgets);
}
}
}; };
/** Widget parts of the smallmap display. */ /** Widget parts of the smallmap display. */

View File

@@ -126,7 +126,7 @@ static void FindStationsAroundSelection()
* If it is needed actually make the window for redrawing. * If it is needed actually make the window for redrawing.
* @param w the window to check. * @param w the window to check.
*/ */
void CheckRedrawStationCoverage(const Window *w) void CheckRedrawStationCoverage(Window *w)
{ {
/* Test if ctrl state changed */ /* Test if ctrl state changed */
static bool _last_ctrl_pressed; static bool _last_ctrl_pressed;

View File

@@ -23,7 +23,7 @@ enum StationCoverageType {
}; };
int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies); int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies);
void CheckRedrawStationCoverage(const Window *w); void CheckRedrawStationCoverage(Window *w);
void ShowSelectStationIfNeeded(const CommandContainer &cmd, TileArea ta); void ShowSelectStationIfNeeded(const CommandContainer &cmd, TileArea ta);
void ShowSelectWaypointIfNeeded(const CommandContainer &cmd, TileArea ta); void ShowSelectWaypointIfNeeded(const CommandContainer &cmd, TileArea ta);

View File

@@ -1514,6 +1514,21 @@ public:
return nullptr; return nullptr;
} }
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override
{
if (this->base_flags & WBF_DIRTY) {
dirty_widgets.push_back(this);
} else {
for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) {
if (child_wid->type == NWID_SPACER) continue;
if (!this->visible[((NWidgetCore*)child_wid)->index]) continue;
child_wid->FillDirtyWidgets(dirty_widgets);
}
}
}
/** /**
* Get the arrangement of the buttons for the toolbar. * Get the arrangement of the buttons for the toolbar.
* @param width the new width of the toolbar. * @param width the new width of the toolbar.

View File

@@ -258,7 +258,7 @@ struct ViewportDrawer {
Point foundation_offset[FOUNDATION_PART_END]; ///< Pixel offset for ground sprites on the foundations. Point foundation_offset[FOUNDATION_PART_END]; ///< Pixel offset for ground sprites on the foundations.
}; };
static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int right, int bottom); static void MarkViewportDirty(ViewPort * const vp, int left, int top, int right, int bottom);
static void MarkRouteStepDirty(RouteStepsMap::const_iterator cit); static void MarkRouteStepDirty(RouteStepsMap::const_iterator cit);
static void MarkRouteStepDirty(const TileIndex tile, uint order_nr); static void MarkRouteStepDirty(const TileIndex tile, uint order_nr);
@@ -372,6 +372,8 @@ void InitializeWindowViewport(Window *w, int x, int y,
vp->map_type = VPMT_BEGIN; vp->map_type = VPMT_BEGIN;
UpdateViewportSizeZoom(vp);
Point pt; Point pt;
if (follow_flags & 0x80000000) { if (follow_flags & 0x80000000) {
@@ -575,6 +577,15 @@ static void DoSetViewportPosition(const Window *w, const int left, const int top
} }
} }
inline void UpdateViewportDirtyBlockLeftMargin(ViewPort *vp)
{
if (vp->zoom >= ZOOM_LVL_DRAW_MAP) {
vp->dirty_block_left_margin = 0;
} else {
vp->dirty_block_left_margin = UnScaleByZoomLower((-vp->virtual_left) & 127, vp->zoom);
}
}
static void SetViewportPosition(Window *w, int x, int y, bool force_update_overlay) static void SetViewportPosition(Window *w, int x, int y, bool force_update_overlay)
{ {
ViewPort *vp = w->viewport; ViewPort *vp = w->viewport;
@@ -585,6 +596,7 @@ static void SetViewportPosition(Window *w, int x, int y, bool force_update_overl
vp->virtual_left = x; vp->virtual_left = x;
vp->virtual_top = y; vp->virtual_top = y;
UpdateViewportDirtyBlockLeftMargin(vp);
if (force_update_overlay || IsViewportOverlayOutsideCachedRegion(w)) RebuildViewportOverlay(w, true); if (force_update_overlay || IsViewportOverlayOutsideCachedRegion(w)) RebuildViewportOverlay(w, true);
@@ -1479,8 +1491,8 @@ static void ViewportAddLandscape()
* - Right column is column of upper_right (rounded up) and one column to the right. * - Right column is column of upper_right (rounded up) and one column to the right.
* Note: Integer-division does not round down for negative numbers, so ensure rounding with another increment/decrement. * Note: Integer-division does not round down for negative numbers, so ensure rounding with another increment/decrement.
*/ */
int left_column = (upper_left.y - upper_left.x) / (int)TILE_SIZE - 2; int left_column = DivTowardsNegativeInf(upper_left.y - upper_left.x, (int)TILE_SIZE) - 1;
int right_column = (upper_right.y - upper_right.x) / (int)TILE_SIZE + 2; int right_column = DivTowardsPositiveInf(upper_right.y - upper_right.x, (int)TILE_SIZE) + 1;
int potential_bridge_height = ZOOM_LVL_BASE * TILE_HEIGHT * _settings_game.construction.max_bridge_height; int potential_bridge_height = ZOOM_LVL_BASE * TILE_HEIGHT * _settings_game.construction.max_bridge_height;
@@ -1488,7 +1500,7 @@ static void ViewportAddLandscape()
* The first row that could possibly be visible is the row above upper_left (if it is at height 0). * The first row that could possibly be visible is the row above upper_left (if it is at height 0).
* Due to integer-division not rounding down for negative numbers, we need another decrement. * Due to integer-division not rounding down for negative numbers, we need another decrement.
*/ */
int row = (upper_left.x + upper_left.y) / (int)TILE_SIZE - 2; int row = DivTowardsNegativeInf(upper_left.y + upper_left.x, (int)TILE_SIZE) - 1;
bool last_row = false; bool last_row = false;
for (; !last_row; row++) { for (; !last_row; row++) {
last_row = true; last_row = true;
@@ -3003,8 +3015,8 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom
_vd.dpi.pitch = old_dpi->pitch; _vd.dpi.pitch = old_dpi->pitch;
_vd.last_child = nullptr; _vd.last_child = nullptr;
int x = UnScaleByZoom(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left; int x = UnScaleByZoomLower(_vd.dpi.left - (vp->virtual_left & mask), vp->zoom) + vp->left;
int y = UnScaleByZoom(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top; int y = UnScaleByZoomLower(_vd.dpi.top - (vp->virtual_top & mask), vp->zoom) + vp->top;
_vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top); _vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top);
@@ -3098,7 +3110,7 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom
* Make sure we don't draw a too big area at a time. * Make sure we don't draw a too big area at a time.
* If we do, the sprite sorter will run into major performance problems and the sprite memory may overflow. * If we do, the sprite sorter will run into major performance problems and the sprite memory may overflow.
*/ */
static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom) void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom)
{ {
if ((vp->zoom < ZOOM_LVL_DRAW_MAP) && ((int64)ScaleByZoom(bottom - top, vp->zoom) * (int64)ScaleByZoom(right - left, vp->zoom) > (int64)(1000000 * ZOOM_LVL_BASE * ZOOM_LVL_BASE))) { if ((vp->zoom < ZOOM_LVL_DRAW_MAP) && ((int64)ScaleByZoom(bottom - top, vp->zoom) * (int64)ScaleByZoom(right - left, vp->zoom) > (int64)(1000000 * ZOOM_LVL_BASE * ZOOM_LVL_BASE))) {
if ((bottom - top) > (right - left)) { if ((bottom - top) > (right - left)) {
@@ -3120,7 +3132,7 @@ static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, in
} }
} }
static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right, int bottom) static inline void ViewportDraw(ViewPort *vp, int left, int top, int right, int bottom)
{ {
if (right <= vp->left || bottom <= vp->top) return; if (right <= vp->left || bottom <= vp->top) return;
@@ -3134,6 +3146,8 @@ static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right
if (top < vp->top) top = vp->top; if (top < vp->top) top = vp->top;
if (bottom > vp->top + vp->height) bottom = vp->top + vp->height; if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
vp->is_drawn = true;
ViewportDrawChk(vp, left, top, right, bottom); ViewportDrawChk(vp, left, top, right, bottom);
} }
@@ -3230,6 +3244,15 @@ void UpdateViewportPosition(Window *w)
} }
} }
void UpdateViewportSizeZoom(ViewPort *vp)
{
vp->dirty_blocks_per_column = CeilDiv(vp->height, vp->GetDirtyBlockHeight());
vp->dirty_blocks_per_row = CeilDiv(vp->width, vp->GetDirtyBlockWidth());
uint size = vp->dirty_blocks_per_row * vp->dirty_blocks_per_column;
vp->dirty_blocks.assign(size, false);
UpdateViewportDirtyBlockLeftMargin(vp);
}
void UpdateActiveScrollingViewport(Window *w) void UpdateActiveScrollingViewport(Window *w)
{ {
if (w && (!_settings_client.gui.show_scrolling_viewport_on_map || w->viewport->zoom >= ZOOM_LVL_DRAW_MAP)) w = nullptr; if (w && (!_settings_client.gui.show_scrolling_viewport_on_map || w->viewport->zoom >= ZOOM_LVL_DRAW_MAP)) w = nullptr;
@@ -3283,7 +3306,7 @@ void UpdateActiveScrollingViewport(Window *w)
* @param bottom Bottom edge of area to repaint * @param bottom Bottom edge of area to repaint
* @ingroup dirty * @ingroup dirty
*/ */
static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int right, int bottom) static void MarkViewportDirty(ViewPort * const vp, int left, int top, int right, int bottom)
{ {
/* Rounding wrt. zoom-out level */ /* Rounding wrt. zoom-out level */
right += (1 << vp->zoom) - 1; right += (1 << vp->zoom) - 1;
@@ -3305,12 +3328,21 @@ static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int
if (top >= vp->virtual_height) return; if (top >= vp->virtual_height) return;
SetDirtyBlocks( uint x = max<int>(0, UnScaleByZoomLower(left, vp->zoom) - vp->dirty_block_left_margin) >> vp->GetDirtyBlockWidthShift();
UnScaleByZoomLower(left, vp->zoom) + vp->left, uint y = UnScaleByZoomLower(top, vp->zoom) >> vp->GetDirtyBlockHeightShift();
UnScaleByZoomLower(top, vp->zoom) + vp->top, uint w = (max<int>(0, UnScaleByZoomLower(right, vp->zoom) - 1 - vp->dirty_block_left_margin) >> vp->GetDirtyBlockWidthShift()) + 1 - x;
UnScaleByZoom(right, vp->zoom) + vp->left + 1, uint h = ((UnScaleByZoom(bottom, vp->zoom) - 1) >> vp->GetDirtyBlockHeightShift()) + 1 - y;
UnScaleByZoom(bottom, vp->zoom) + vp->top + 1
); uint column_skip = vp->dirty_blocks_per_column - h;
uint pos = (x * vp->dirty_blocks_per_column) + y;
for (uint i = 0; i < w; i++) {
for (uint j = 0; j < h; j++) {
vp->dirty_blocks[pos] = true;
pos++;
}
pos += column_skip;
}
vp->is_dirty = true;
} }
/** /**
@@ -3324,7 +3356,7 @@ static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int
*/ */
void MarkAllViewportsDirty(int left, int top, int right, int bottom, const ZoomLevel mark_dirty_if_zoomlevel_is_below) void MarkAllViewportsDirty(int left, int top, int right, int bottom, const ZoomLevel mark_dirty_if_zoomlevel_is_below)
{ {
for (const ViewPort * const vp : _viewport_window_cache) { for (ViewPort * const vp : _viewport_window_cache) {
if (vp->zoom >= mark_dirty_if_zoomlevel_is_below) continue; if (vp->zoom >= mark_dirty_if_zoomlevel_is_below) continue;
MarkViewportDirty(vp, left, top, right, bottom); MarkViewportDirty(vp, left, top, right, bottom);
} }
@@ -3341,7 +3373,7 @@ static void MarkRouteStepDirty(const TileIndex tile, uint order_nr)
assert(tile != INVALID_TILE); assert(tile != INVALID_TILE);
const Point pt = RemapCoords2(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2); const Point pt = RemapCoords2(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2);
const int char_height = GetCharacterHeight(FS_SMALL) + 1; const int char_height = GetCharacterHeight(FS_SMALL) + 1;
for (const ViewPort * const vp : _viewport_window_cache) { for (ViewPort * const vp : _viewport_window_cache) {
const int half_width = ScaleByZoom((_vp_route_step_width / 2) + 1, vp->zoom); const int half_width = ScaleByZoom((_vp_route_step_width / 2) + 1, vp->zoom);
const int height = ScaleByZoom(_vp_route_step_height_top + char_height * order_nr + _vp_route_step_height_bottom, vp->zoom); const int height = ScaleByZoom(_vp_route_step_height_top + char_height * order_nr + _vp_route_step_height_bottom, vp->zoom);
MarkViewportDirty(vp, pt.x - half_width, pt.y - height, pt.x + half_width, pt.y); MarkViewportDirty(vp, pt.x - half_width, pt.y - height, pt.x + half_width, pt.y);
@@ -3370,7 +3402,7 @@ void MarkAllViewportMapsDirty(int left, int top, int right, int bottom)
{ {
Window *w; Window *w;
FOR_ALL_WINDOWS_FROM_BACK(w) { FOR_ALL_WINDOWS_FROM_BACK(w) {
const ViewPort *vp = w->viewport; ViewPort *vp = w->viewport;
if (vp != nullptr && vp->zoom >= ZOOM_LVL_DRAW_MAP) { if (vp != nullptr && vp->zoom >= ZOOM_LVL_DRAW_MAP) {
assert(vp->width != 0); assert(vp->width != 0);
MarkViewportDirty(vp, left, top, right, bottom); MarkViewportDirty(vp, left, top, right, bottom);

View File

@@ -29,6 +29,7 @@ ViewPort *IsPtInWindowViewport(const Window *w, int x, int y);
Point TranslateXYToTileCoord(const ViewPort *vp, int x, int y, bool clamp_to_map = true); Point TranslateXYToTileCoord(const ViewPort *vp, int x, int y, bool clamp_to_map = true);
Point GetTileBelowCursor(); Point GetTileBelowCursor();
void UpdateViewportPosition(Window *w); void UpdateViewportPosition(Window *w);
void UpdateViewportSizeZoom(ViewPort *vp);
void MarkAllViewportsDirty(int left, int top, int right, int bottom, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END); void MarkAllViewportsDirty(int left, int top, int right, int bottom, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END);
void MarkAllViewportMapsDirty(int left, int top, int right, int bottom); void MarkAllViewportMapsDirty(int left, int top, int right, int bottom);

View File

@@ -14,6 +14,8 @@
#include "strings_type.h" #include "strings_type.h"
#include "table/strings.h" #include "table/strings.h"
#include <vector>
class LinkGraphOverlay; class LinkGraphOverlay;
enum ViewportMapType { enum ViewportMapType {
@@ -45,6 +47,35 @@ struct ViewPort {
ViewportMapType map_type; ///< Rendering type ViewportMapType map_type; ///< Rendering type
LinkGraphOverlay *overlay; LinkGraphOverlay *overlay;
std::vector<bool> dirty_blocks;
uint dirty_blocks_per_column;
uint dirty_blocks_per_row;
uint8 dirty_block_left_margin;
bool is_dirty = false;
bool is_drawn = false;
uint GetDirtyBlockWidthShift() const { return this->GetDirtyBlockShift(); }
uint GetDirtyBlockHeightShift() const { return this->GetDirtyBlockShift(); }
uint GetDirtyBlockWidth() const { return 1 << this->GetDirtyBlockWidthShift(); }
uint GetDirtyBlockHeight() const { return 1 << this->GetDirtyBlockHeightShift(); }
void ClearDirty()
{
if (this->is_dirty) {
this->dirty_blocks.assign(this->dirty_blocks.size(), false);
this->is_dirty = false;
}
this->is_drawn = false;
}
private:
uint GetDirtyBlockShift() const
{
if (this->zoom >= ZOOM_LVL_DRAW_MAP) return 3;
if (this->zoom >= ZOOM_LVL_OUT_8X) return 4;
return 7 - this->zoom;
}
}; };
/** Margins for the viewport sign */ /** Margins for the viewport sign */

View File

@@ -770,11 +770,10 @@ NWidgetBase::NWidgetBase(WidgetType tp) : ZeroedMemoryAllocator()
* Mark the widget as 'dirty' (in need of repaint). * Mark the widget as 'dirty' (in need of repaint).
* @param w Window owning the widget. * @param w Window owning the widget.
*/ */
void NWidgetBase::SetDirty(const Window *w) const void NWidgetBase::SetDirty(Window *w)
{ {
int abs_left = w->left + this->pos_x; this->base_flags |= WBF_DIRTY;
int abs_top = w->top + this->pos_y; w->flags |= WF_DIRTY;
SetDirtyBlocks(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
} }
/** /**
@@ -905,6 +904,11 @@ NWidgetCore *NWidgetCore::GetWidgetFromPos(int x, int y)
return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : nullptr; return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : nullptr;
} }
void NWidgetCore::FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets)
{
if (this->base_flags & WBF_DIRTY) dirty_widgets.push_back(this);
}
/** /**
* Constructor container baseclass. * Constructor container baseclass.
* @param tp Type of the container. * @param tp Type of the container.
@@ -1049,6 +1053,7 @@ void NWidgetStacked::FillNestedArray(NWidgetBase **array, uint length)
void NWidgetStacked::Draw(const Window *w) void NWidgetStacked::Draw(const Window *w)
{ {
if (this->IsOutsideDrawArea()) return; if (this->IsOutsideDrawArea()) return;
this->base_flags &= ~WBF_DIRTY;
if (this->shown_plane >= SZSP_BEGIN) return; if (this->shown_plane >= SZSP_BEGIN) return;
int plane = 0; int plane = 0;
@@ -1076,6 +1081,21 @@ NWidgetCore *NWidgetStacked::GetWidgetFromPos(int x, int y)
return nullptr; return nullptr;
} }
void NWidgetStacked::FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets)
{
if (this->base_flags & WBF_DIRTY) {
dirty_widgets.push_back(this);
} else {
int plane = 0;
for (NWidgetBase *child_wid = this->head; child_wid != nullptr; plane++, child_wid = child_wid->next) {
if (plane == this->shown_plane) {
child_wid->FillDirtyWidgets(dirty_widgets);
return;
}
}
}
}
/** /**
* Select which plane to show (for #NWID_SELECTION only). * Select which plane to show (for #NWID_SELECTION only).
* @param plane Plane number to display. * @param plane Plane number to display.
@@ -1109,6 +1129,7 @@ void NWidgetPIPContainer::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post)
void NWidgetPIPContainer::Draw(const Window *w) void NWidgetPIPContainer::Draw(const Window *w)
{ {
if (this->IsOutsideDrawArea()) return; if (this->IsOutsideDrawArea()) return;
this->base_flags &= ~WBF_DIRTY;
for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) { for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) {
child_wid->Draw(w); child_wid->Draw(w);
} }
@@ -1125,6 +1146,17 @@ NWidgetCore *NWidgetPIPContainer::GetWidgetFromPos(int x, int y)
return nullptr; return nullptr;
} }
void NWidgetPIPContainer::FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets)
{
if (this->base_flags & WBF_DIRTY) {
dirty_widgets.push_back(this);
} else {
for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) {
child_wid->FillDirtyWidgets(dirty_widgets);
}
}
}
/** Horizontal container widget. */ /** Horizontal container widget. */
NWidgetHorizontal::NWidgetHorizontal(NWidContainerFlags flags) : NWidgetPIPContainer(NWID_HORIZONTAL, flags) NWidgetHorizontal::NWidgetHorizontal(NWidContainerFlags flags) : NWidgetPIPContainer(NWID_HORIZONTAL, flags)
{ {
@@ -1456,7 +1488,7 @@ void NWidgetSpacer::Draw(const Window *w)
/* Spacer widget is never visible. */ /* Spacer widget is never visible. */
} }
void NWidgetSpacer::SetDirty(const Window *w) const void NWidgetSpacer::SetDirty(Window *w)
{ {
/* Spacer widget never need repainting. */ /* Spacer widget never need repainting. */
} }
@@ -1466,6 +1498,11 @@ NWidgetCore *NWidgetSpacer::GetWidgetFromPos(int x, int y)
return nullptr; return nullptr;
} }
void NWidgetSpacer::FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets)
{
/* Spacer widget never need repainting. */
}
NWidgetMatrix::NWidgetMatrix() : NWidgetPIPContainer(NWID_MATRIX, NC_EQUALSIZE), index(-1), clicked(-1), count(-1) NWidgetMatrix::NWidgetMatrix() : NWidgetPIPContainer(NWID_MATRIX, NC_EQUALSIZE), index(-1), clicked(-1), count(-1)
{ {
} }
@@ -1623,9 +1660,17 @@ NWidgetCore *NWidgetMatrix::GetWidgetFromPos(int x, int y)
return child->GetWidgetFromPos(x, y); return child->GetWidgetFromPos(x, y);
} }
void NWidgetMatrix::FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets)
{
if (this->base_flags & WBF_DIRTY) {
dirty_widgets.push_back(this);
}
}
/* virtual */ void NWidgetMatrix::Draw(const Window *w) /* virtual */ void NWidgetMatrix::Draw(const Window *w)
{ {
if (this->IsOutsideDrawArea()) return; if (this->IsOutsideDrawArea()) return;
this->base_flags &= ~WBF_DIRTY;
/* Fill the background. */ /* Fill the background. */
GfxFillRect(this->pos_x, this->pos_y, this->pos_x + this->current_x - 1, this->pos_y + this->current_y - 1, _colour_gradient[this->colour & 0xF][5]); GfxFillRect(this->pos_x, this->pos_y, this->pos_x + this->current_x - 1, this->pos_y + this->current_y - 1, _colour_gradient[this->colour & 0xF][5]);
@@ -1831,6 +1876,8 @@ void NWidgetBackground::FillNestedArray(NWidgetBase **array, uint length)
void NWidgetBackground::Draw(const Window *w) void NWidgetBackground::Draw(const Window *w)
{ {
if (this->IsOutsideDrawArea()) return; if (this->IsOutsideDrawArea()) return;
this->base_flags &= ~WBF_DIRTY;
if (this->current_x == 0 || this->current_y == 0) return; if (this->current_x == 0 || this->current_y == 0) return;
Rect r; Rect r;
@@ -1880,6 +1927,15 @@ NWidgetCore *NWidgetBackground::GetWidgetFromPos(int x, int y)
return nwid; return nwid;
} }
void NWidgetBackground::FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets)
{
if (this->base_flags & WBF_DIRTY) {
dirty_widgets.push_back(this);
} else {
if (this->child != nullptr) this->child->FillDirtyWidgets(dirty_widgets);
}
}
NWidgetBase *NWidgetBackground::GetWidgetOfType(WidgetType tp) NWidgetBase *NWidgetBackground::GetWidgetOfType(WidgetType tp)
{ {
NWidgetBase *nwid = nullptr; NWidgetBase *nwid = nullptr;
@@ -1906,6 +1962,8 @@ void NWidgetViewport::SetupSmallestSize(Window *w, bool init_array)
void NWidgetViewport::Draw(const Window *w) void NWidgetViewport::Draw(const Window *w)
{ {
if (this->IsOutsideDrawArea()) return; if (this->IsOutsideDrawArea()) return;
this->base_flags &= ~WBF_DIRTY;
if (this->disp_flags & ND_NO_TRANSPARENCY) { if (this->disp_flags & ND_NO_TRANSPARENCY) {
TransparencyOptionBits to_backup = _transparency_opt; TransparencyOptionBits to_backup = _transparency_opt;
_transparency_opt &= (1 << TO_SIGNS) | (1 << TO_LOADING); // Disable all transparency, except textual stuff _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_LOADING); // Disable all transparency, except textual stuff
@@ -1931,6 +1989,7 @@ void NWidgetViewport::Draw(const Window *w)
void NWidgetViewport::InitializeViewport(Window *w, uint32 follow_flags, ZoomLevel zoom) void NWidgetViewport::InitializeViewport(Window *w, uint32 follow_flags, ZoomLevel zoom)
{ {
InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, follow_flags, zoom); InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, follow_flags, zoom);
w->viewport_widget = this;
} }
/** /**
@@ -1948,6 +2007,7 @@ void NWidgetViewport::UpdateViewportCoordinates(Window *w)
vp->virtual_width = ScaleByZoom(vp->width, vp->zoom); vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
vp->virtual_height = ScaleByZoom(vp->height, vp->zoom); vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
UpdateViewportSizeZoom(vp);
} }
} }
@@ -2030,6 +2090,8 @@ void NWidgetScrollbar::SetupSmallestSize(Window *w, bool init_array)
void NWidgetScrollbar::Draw(const Window *w) void NWidgetScrollbar::Draw(const Window *w)
{ {
if (this->IsOutsideDrawArea()) return; if (this->IsOutsideDrawArea()) return;
this->base_flags &= ~WBF_DIRTY;
if (this->current_x == 0 || this->current_y == 0) return; if (this->current_x == 0 || this->current_y == 0) return;
Rect r; Rect r;
@@ -2398,6 +2460,8 @@ void NWidgetLeaf::SetupSmallestSize(Window *w, bool init_array)
void NWidgetLeaf::Draw(const Window *w) void NWidgetLeaf::Draw(const Window *w)
{ {
if (this->IsOutsideDrawArea()) return; if (this->IsOutsideDrawArea()) return;
this->base_flags &= ~WBF_DIRTY;
if (this->current_x == 0 || this->current_y == 0) return; if (this->current_x == 0 || this->current_y == 0) return;
/* Setup a clipping rectangle... */ /* Setup a clipping rectangle... */

View File

@@ -18,6 +18,8 @@
#include "gfx_type.h" #include "gfx_type.h"
#include "window_type.h" #include "window_type.h"
#include <vector>
static const int WIDGET_LIST_END = -1; ///< indicate the end of widgets' list for vararg functions static const int WIDGET_LIST_END = -1; ///< indicate the end of widgets' list for vararg functions
/** Bits of the #WWT_MATRIX widget data. */ /** Bits of the #WWT_MATRIX widget data. */
@@ -42,7 +44,7 @@ enum ArrowWidgetValues {
/** /**
* Window widget types, nested widget types, and nested widget part types. * Window widget types, nested widget types, and nested widget part types.
*/ */
enum WidgetType { enum WidgetType : uint8 {
/* Window widget types. */ /* Window widget types. */
WWT_EMPTY, ///< Empty widget, place holder to reserve space in widget array WWT_EMPTY, ///< Empty widget, place holder to reserve space in widget array
@@ -106,6 +108,14 @@ enum WidgetType {
NWID_PUSHBUTTON_DROPDOWN = NWID_BUTTON_DROPDOWN | WWB_PUSHBUTTON, NWID_PUSHBUTTON_DROPDOWN = NWID_BUTTON_DROPDOWN | WWB_PUSHBUTTON,
}; };
/**
* Base widget flags.
*/
enum WidgetBaseFlags : uint8 {
WBF_DIRTY = 1 << 0, ///< Widget is dirty.
};
DECLARE_ENUM_AS_BIT_SET(WidgetBaseFlags)
/** Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition() */ /** Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition() */
enum SizingType { enum SizingType {
ST_SMALLEST, ///< Initialize nested widget tree to smallest size. Also updates \e current_x and \e current_y. ST_SMALLEST, ///< Initialize nested widget tree to smallest size. Also updates \e current_x and \e current_y.
@@ -157,9 +167,11 @@ public:
inline uint GetVerticalStepSize(SizingType sizing) const; inline uint GetVerticalStepSize(SizingType sizing) const;
virtual void Draw(const Window *w) = 0; virtual void Draw(const Window *w) = 0;
virtual void SetDirty(const Window *w) const; virtual void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) = 0;
virtual void SetDirty(Window *w);
WidgetType type; ///< Type of the widget / nested widget. WidgetType type; ///< Type of the widget / nested widget.
WidgetBaseFlags base_flags; ///< Widget base flags
uint fill_x; ///< Horizontal fill stepsize (from initial size, \c 0 means not resizable). uint fill_x; ///< Horizontal fill stepsize (from initial size, \c 0 means not resizable).
uint fill_y; ///< Vertical fill stepsize (from initial size, \c 0 means not resizable). uint fill_y; ///< Vertical fill stepsize (from initial size, \c 0 means not resizable).
uint resize_x; ///< Horizontal resize step (\c 0 means not resizable). uint resize_x; ///< Horizontal resize step (\c 0 means not resizable).
@@ -304,6 +316,7 @@ public:
bool IsHighlighted() const override; bool IsHighlighted() const override;
TextColour GetHighlightColour() const override; TextColour GetHighlightColour() const override;
void SetHighlighted(TextColour highlight_colour) override; void SetHighlighted(TextColour highlight_colour) override;
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override;
NWidgetDisplay disp_flags; ///< Flags that affect display and interaction with the widget. NWidgetDisplay disp_flags; ///< Flags that affect display and interaction with the widget.
Colours colour; ///< Colour of this widget. Colours colour; ///< Colour of this widget.
@@ -420,6 +433,7 @@ public:
void Draw(const Window *w) override; void Draw(const Window *w) override;
NWidgetCore *GetWidgetFromPos(int x, int y) override; NWidgetCore *GetWidgetFromPos(int x, int y) override;
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override;
void SetDisplayedPlane(int plane); void SetDisplayedPlane(int plane);
@@ -445,6 +459,7 @@ public:
void Draw(const Window *w) override; void Draw(const Window *w) override;
NWidgetCore *GetWidgetFromPos(int x, int y) override; NWidgetCore *GetWidgetFromPos(int x, int y) override;
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override;
protected: protected:
NWidContainerFlags flags; ///< Flags of the container. NWidContainerFlags flags; ///< Flags of the container.
@@ -511,6 +526,7 @@ public:
void FillNestedArray(NWidgetBase **array, uint length) override; void FillNestedArray(NWidgetBase **array, uint length) override;
NWidgetCore *GetWidgetFromPos(int x, int y) override; NWidgetCore *GetWidgetFromPos(int x, int y) override;
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override;
void Draw(const Window *w) override; void Draw(const Window *w) override;
protected: protected:
int index; ///< If non-negative, index in the #Window::nested_array. int index; ///< If non-negative, index in the #Window::nested_array.
@@ -540,8 +556,9 @@ public:
void FillNestedArray(NWidgetBase **array, uint length) override; void FillNestedArray(NWidgetBase **array, uint length) override;
void Draw(const Window *w) override; void Draw(const Window *w) override;
void SetDirty(const Window *w) const override; void SetDirty(Window *w) override;
NWidgetCore *GetWidgetFromPos(int x, int y) override; NWidgetCore *GetWidgetFromPos(int x, int y) override;
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override;
}; };
/** /**
@@ -564,6 +581,7 @@ public:
void Draw(const Window *w) override; void Draw(const Window *w) override;
NWidgetCore *GetWidgetFromPos(int x, int y) override; NWidgetCore *GetWidgetFromPos(int x, int y) override;
NWidgetBase *GetWidgetOfType(WidgetType tp) override; NWidgetBase *GetWidgetOfType(WidgetType tp) override;
void FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets) override;
private: private:
NWidgetPIPContainer *child; ///< Child widget. NWidgetPIPContainer *child; ///< Child widget.

View File

@@ -607,7 +607,7 @@ void Window::RaiseButtons(bool autoraise)
* Invalidate a widget, i.e. mark it as being changed and in need of redraw. * Invalidate a widget, i.e. mark it as being changed and in need of redraw.
* @param widget_index the widget to redraw. * @param widget_index the widget to redraw.
*/ */
void Window::SetWidgetDirty(byte widget_index) const void Window::SetWidgetDirty(byte widget_index)
{ {
/* Sometimes this function is called before the window is even fully initialized */ /* Sometimes this function is called before the window is even fully initialized */
if (this->nested_array == nullptr) return; if (this->nested_array == nullptr) return;
@@ -876,27 +876,6 @@ static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
} }
} }
/**
* Returns whether a window may be shown or not.
* @param w The window to consider.
* @return True iff it may be shown, otherwise false.
*/
static bool MayBeShown(const Window *w)
{
/* If we're not modal, everything is okay. */
if (!HasModalProgress()) return true;
switch (w->window_class) {
case WC_MAIN_WINDOW: ///< The background, i.e. the game.
case WC_MODAL_PROGRESS: ///< The actual progress window.
case WC_CONFIRM_POPUP_QUERY: ///< The abort window.
return true;
default:
return false;
}
}
/** /**
* Generate repaint events for the visible part of window w within the rectangle. * Generate repaint events for the visible part of window w within the rectangle.
* *
@@ -908,8 +887,9 @@ static bool MayBeShown(const Window *w)
* @param top Top edge of the rectangle that should be repainted * @param top Top edge of the rectangle that should be repainted
* @param right Right edge of the rectangle that should be repainted * @param right Right edge of the rectangle that should be repainted
* @param bottom Bottom edge of the rectangle that should be repainted * @param bottom Bottom edge of the rectangle that should be repainted
* @param gfx_dirty Whether to mark gfx dirty
*/ */
static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom) void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom, bool gfx_dirty)
{ {
const Window *v; const Window *v;
FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) { FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
@@ -922,26 +902,26 @@ static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bo
int x; int x;
if (left < (x = v->left)) { if (left < (x = v->left)) {
DrawOverlappedWindow(w, left, top, x, bottom); DrawOverlappedWindow(w, left, top, x, bottom, gfx_dirty);
DrawOverlappedWindow(w, x, top, right, bottom); DrawOverlappedWindow(w, x, top, right, bottom, gfx_dirty);
return; return;
} }
if (right > (x = v->left + v->width)) { if (right > (x = v->left + v->width)) {
DrawOverlappedWindow(w, left, top, x, bottom); DrawOverlappedWindow(w, left, top, x, bottom, gfx_dirty);
DrawOverlappedWindow(w, x, top, right, bottom); DrawOverlappedWindow(w, x, top, right, bottom, gfx_dirty);
return; return;
} }
if (top < (x = v->top)) { if (top < (x = v->top)) {
DrawOverlappedWindow(w, left, top, right, x); DrawOverlappedWindow(w, left, top, right, x, gfx_dirty);
DrawOverlappedWindow(w, left, x, right, bottom); DrawOverlappedWindow(w, left, x, right, bottom, gfx_dirty);
return; return;
} }
if (bottom > (x = v->top + v->height)) { if (bottom > (x = v->top + v->height)) {
DrawOverlappedWindow(w, left, top, right, x); DrawOverlappedWindow(w, left, top, right, x, gfx_dirty);
DrawOverlappedWindow(w, left, x, right, bottom); DrawOverlappedWindow(w, left, x, right, bottom, gfx_dirty);
return; return;
} }
@@ -959,6 +939,10 @@ static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bo
dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top); dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
dp->zoom = ZOOM_LVL_NORMAL; dp->zoom = ZOOM_LVL_NORMAL;
w->OnPaint(); w->OnPaint();
if (gfx_dirty) {
VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top);
UnsetDirtyBlocks(left, top, right, bottom);
}
} }
/** /**
@@ -984,7 +968,7 @@ void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
left < w->left + w->width && left < w->left + w->width &&
top < w->top + w->height) { top < w->top + w->height) {
/* Window w intersects with the rectangle => needs repaint */ /* Window w intersects with the rectangle => needs repaint */
DrawOverlappedWindow(w, max(left, w->left), max(top, w->top), min(right, w->left + w->width), min(bottom, w->top + w->height)); DrawOverlappedWindow(w, max(left, w->left), max(top, w->top), min(right, w->left + w->width), min(bottom, w->top + w->height), false);
} }
} }
_cur_dpi = old_dpi; _cur_dpi = old_dpi;
@@ -994,7 +978,16 @@ void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
* Mark entire window as dirty (in need of re-paint) * Mark entire window as dirty (in need of re-paint)
* @ingroup dirty * @ingroup dirty
*/ */
void Window::SetDirty() const void Window::SetDirty()
{
this->flags |= WF_DIRTY;
}
/**
* Mark entire window as dirty (in need of re-paint)
* @ingroup dirty
*/
void Window::SetDirtyAsBlocks()
{ {
SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height); SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
} }
@@ -1007,7 +1000,7 @@ void Window::SetDirty() const
*/ */
void Window::ReInit(int rx, int ry) void Window::ReInit(int rx, int ry)
{ {
this->SetDirty(); // Mark whole current window as dirty. this->SetDirtyAsBlocks(); // Mark whole current window as dirty.
/* Save current size. */ /* Save current size. */
int window_width = this->width; int window_width = this->width;
@@ -1120,7 +1113,7 @@ Window::~Window()
if (this->viewport != nullptr) DeleteWindowViewport(this); if (this->viewport != nullptr) DeleteWindowViewport(this);
this->SetDirty(); this->SetDirtyAsBlocks();
free(this->nested_array); // Contents is released through deletion of #nested_root. free(this->nested_array); // Contents is released through deletion of #nested_root.
delete this->nested_root; delete this->nested_root;
@@ -2171,7 +2164,7 @@ void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y)); if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y));
} }
w->SetDirty(); w->SetDirtyAsBlocks();
uint new_xinc = max(0, (w->nested_root->resize_x == 0) ? 0 : (int)(w->nested_root->current_x - w->nested_root->smallest_x) + delta_x); uint new_xinc = max(0, (w->nested_root->resize_x == 0) ? 0 : (int)(w->nested_root->current_x - w->nested_root->smallest_x) + delta_x);
uint new_yinc = max(0, (w->nested_root->resize_y == 0) ? 0 : (int)(w->nested_root->current_y - w->nested_root->smallest_y) + delta_y); uint new_yinc = max(0, (w->nested_root->resize_y == 0) ? 0 : (int)(w->nested_root->current_y - w->nested_root->smallest_y) + delta_y);
@@ -2236,7 +2229,7 @@ static EventState HandleWindowDragging()
break; break;
} }
w->SetDirty(); w->SetDirtyAsBlocks();
int x = _cursor.pos.x + _drag_delta.x; int x = _cursor.pos.x + _drag_delta.x;
int y = _cursor.pos.y + _drag_delta.y; int y = _cursor.pos.y + _drag_delta.y;
@@ -2371,7 +2364,7 @@ static EventState HandleWindowDragging()
_drag_delta.y += y; _drag_delta.y += y;
if ((w->flags & WF_SIZING_LEFT) && x != 0) { if ((w->flags & WF_SIZING_LEFT) && x != 0) {
_drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position. _drag_delta.x -= x; // x > 0 -> window gets longer -> left-edge moves to left -> subtract x to get new position.
w->SetDirty(); w->SetDirtyAsBlocks();
w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount. w->left -= x; // If dragging left edge, move left window edge in opposite direction by the same amount.
/* ResizeWindow() below ensures marking new position as dirty. */ /* ResizeWindow() below ensures marking new position as dirty. */
} else { } else {
@@ -3282,7 +3275,7 @@ void UpdateWindows()
*/ */
void SetWindowDirty(WindowClass cls, WindowNumber number) void SetWindowDirty(WindowClass cls, WindowNumber number)
{ {
const Window *w; Window *w;
FOR_ALL_WINDOWS_FROM_BACK(w) { FOR_ALL_WINDOWS_FROM_BACK(w) {
if (w->window_class == cls && w->window_number == number) w->SetDirty(); if (w->window_class == cls && w->window_number == number) w->SetDirty();
} }
@@ -3296,7 +3289,7 @@ void SetWindowDirty(WindowClass cls, WindowNumber number)
*/ */
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index) void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
{ {
const Window *w; Window *w;
FOR_ALL_WINDOWS_FROM_BACK(w) { FOR_ALL_WINDOWS_FROM_BACK(w) {
if (w->window_class == cls && w->window_number == number) { if (w->window_class == cls && w->window_number == number) {
w->SetWidgetDirty(widget_index); w->SetWidgetDirty(widget_index);
@@ -3504,8 +3497,6 @@ restart_search:
goto restart_search; goto restart_search;
} }
} }
FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
} }
/** Delete all always on-top windows to get an empty screen */ /** Delete all always on-top windows to get an empty screen */

View File

@@ -240,7 +240,7 @@ enum SortButtonState {
/** /**
* Window flags. * Window flags.
*/ */
enum WindowFlags { enum WindowFlags : uint16 {
WF_TIMEOUT = 1 << 0, ///< Window timeout counter. WF_TIMEOUT = 1 << 0, ///< Window timeout counter.
WF_DRAGGING = 1 << 3, ///< Window is being dragged. WF_DRAGGING = 1 << 3, ///< Window is being dragged.
@@ -252,6 +252,8 @@ enum WindowFlags {
WF_WHITE_BORDER = 1 << 8, ///< Window white border counter bit mask. WF_WHITE_BORDER = 1 << 8, ///< Window white border counter bit mask.
WF_HIGHLIGHTED = 1 << 9, ///< Window has a widget that has a highlight. WF_HIGHLIGHTED = 1 << 9, ///< Window has a widget that has a highlight.
WF_CENTERED = 1 << 10, ///< Window is centered and shall stay centered after ReInit. WF_CENTERED = 1 << 10, ///< Window is centered and shall stay centered after ReInit.
WF_DIRTY = 1 << 11, ///< Whole window is dirty, and requires repainting.
WF_WIDGETS_DIRTY = 1 << 12, ///< One or more widgets are dirty, and require repainting.
}; };
DECLARE_ENUM_AS_BIT_SET(WindowFlags) DECLARE_ENUM_AS_BIT_SET(WindowFlags)
@@ -375,7 +377,8 @@ public:
Owner owner; ///< The owner of the content shown in this window. Company colour is acquired from this variable. Owner owner; ///< The owner of the content shown in this window. Company colour is acquired from this variable.
ViewportData *viewport; ///< Pointer to viewport data, if present. ViewportData *viewport; ///< Pointer to viewport data, if present.
const NWidgetCore *nested_focus; ///< Currently focused nested widget, or \c nullptr if no nested widget has focus. NWidgetViewport *viewport_widget; ///< Pointer to viewport widget, if present.
NWidgetCore *nested_focus; ///< Currently focused nested widget, or \c nullptr if no nested widget has focus.
SmallMap<int, QueryString*> querystrings; ///< QueryString associated to WWT_EDITBOX widgets. SmallMap<int, QueryString*> querystrings; ///< QueryString associated to WWT_EDITBOX widgets.
NWidgetBase *nested_root; ///< Root of the nested tree. NWidgetBase *nested_root; ///< Root of the nested tree.
NWidgetBase **nested_array; ///< Array of pointers into the tree. Do not access directly, use #Window::GetWidget() instead. NWidgetBase **nested_array; ///< Array of pointers into the tree. Do not access directly, use #Window::GetWidget() instead.
@@ -557,7 +560,7 @@ public:
void RaiseButtons(bool autoraise = false); void RaiseButtons(bool autoraise = false);
void CDECL SetWidgetsDisabledState(bool disab_stat, int widgets, ...); void CDECL SetWidgetsDisabledState(bool disab_stat, int widgets, ...);
void CDECL SetWidgetsLoweredState(bool lowered_stat, int widgets, ...); void CDECL SetWidgetsLoweredState(bool lowered_stat, int widgets, ...);
void SetWidgetDirty(byte widget_index) const; void SetWidgetDirty(byte widget_index);
void DrawWidgets() const; void DrawWidgets() const;
void DrawViewport() const; void DrawViewport() const;
@@ -566,7 +569,8 @@ public:
void DeleteChildWindows(WindowClass wc = WC_INVALID) const; void DeleteChildWindows(WindowClass wc = WC_INVALID) const;
void SetDirty() const; void SetDirty();
void SetDirtyAsBlocks();
void ReInit(int rx = 0, int ry = 0); void ReInit(int rx = 0, int ry = 0);
/** Is window shaded currently? */ /** Is window shaded currently? */
@@ -1000,4 +1004,26 @@ void SetFocusedWindow(Window *w);
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y); void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y);
/**
* Returns whether a window may be shown or not.
* @param w The window to consider.
* @return True iff it may be shown, otherwise false.
*/
inline bool MayBeShown(const Window *w)
{
/* If we're not modal, everything is okay. */
extern bool _in_modal_progress;
if (likely(!_in_modal_progress)) return true;
switch (w->window_class) {
case WC_MAIN_WINDOW: ///< The background, i.e. the game.
case WC_MODAL_PROGRESS: ///< The actual progress window.
case WC_CONFIRM_POPUP_QUERY: ///< The abort window.
return true;
default:
return false;
}
}
#endif /* WINDOW_GUI_H */ #endif /* WINDOW_GUI_H */