Port dirty block handling from JGRPP
This commit is contained in:
@@ -1812,6 +1812,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. */
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ void BuildLandLegend();
|
|||||||
void BuildOwnerLegend();
|
void BuildOwnerLegend();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//struct Mi
|
||||||
|
// inline uint16 Kdtree_MinimapSignXYFunc(TownID tid, int dim) { return (dim == 0) ? TileX(Town::Get(tid)->xy) : TileY(Town::Get(tid)->xy); }
|
||||||
|
// typedef Kdtree<TownID, decltype(&Kdtree_TownXYFunc), uint16, int> TownKdtree;
|
||||||
|
|
||||||
|
|
||||||
class NWidgetSmallmapDisplay;
|
class NWidgetSmallmapDisplay;
|
||||||
|
|
||||||
/** Class managing the smallmap window. */
|
/** Class managing the smallmap window. */
|
||||||
@@ -178,6 +184,7 @@ protected:
|
|||||||
void DrawMapIndicators() const;
|
void DrawMapIndicators() const;
|
||||||
void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, int y, int end_y, Blitter *blitter) const;
|
void DrawSmallMapColumn(void *dst, uint xc, uint yc, int pitch, int reps, int start_pos, int end_pos, int y, int end_y, Blitter *blitter) const;
|
||||||
void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const;
|
void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const;
|
||||||
|
void DrawIndustryProduction(const DrawPixelInfo *dpi) const;
|
||||||
void DrawTowns(const DrawPixelInfo *dpi) const;
|
void DrawTowns(const DrawPixelInfo *dpi) const;
|
||||||
void DrawSmallMap(DrawPixelInfo *dpi) const;
|
void DrawSmallMap(DrawPixelInfo *dpi) const;
|
||||||
|
|
||||||
|
|||||||
@@ -507,21 +507,21 @@ bool ShowStationRatingTooltip(Window *parent, const Station *st, const CargoSpec
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* copied from window.cpp */
|
/* copied from window.cpp */
|
||||||
static bool MayBeShown(const Window *w)
|
// static bool MayBeShown(const Window *w)
|
||||||
{
|
// {
|
||||||
/* If we're not modal, everything is okay. */
|
// /* If we're not modal, everything is okay. */
|
||||||
if (!HasModalProgress()) return true;
|
// if (!HasModalProgress()) return true;
|
||||||
|
|
||||||
switch (w->window_class) {
|
// switch (w->window_class) {
|
||||||
case WC_MAIN_WINDOW: ///< The background, i.e. the game.
|
// case WC_MAIN_WINDOW: ///< The background, i.e. the game.
|
||||||
case WC_MODAL_PROGRESS: ///< The actual progress window.
|
// case WC_MODAL_PROGRESS: ///< The actual progress window.
|
||||||
case WC_CONFIRM_POPUP_QUERY: ///< The abort window.
|
// case WC_CONFIRM_POPUP_QUERY: ///< The abort window.
|
||||||
return true;
|
// return true;
|
||||||
|
|
||||||
default:
|
// default:
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
Window *FindHoverableWindowFromPt(int x, int y)
|
Window *FindHoverableWindowFromPt(int x, int y)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2520,6 +2520,26 @@ DEF_CONSOLE_CMD(ConDumpInfo)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF_CONSOLE_CMD(ConGfxDebug)
|
||||||
|
{
|
||||||
|
if (argc < 1 || argc > 2) {
|
||||||
|
IConsolePrint(CC_HELP, "Debug: gfx flags. Usage: 'gfx_debug [<flags>]'");
|
||||||
|
IConsolePrint(CC_HELP, " 1: GDF_SHOW_WINDOW_DIRTY");
|
||||||
|
IConsolePrint(CC_HELP, " 2: GDF_SHOW_WIDGET_DIRTY");
|
||||||
|
IConsolePrint(CC_HELP, " 4: GDF_SHOW_RECT_DIRTY");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern uint32 _gfx_debug_flags;
|
||||||
|
if (argc == 1) {
|
||||||
|
IConsolePrint(CC_DEFAULT, "Gfx debug flags: {:X}", _gfx_debug_flags);
|
||||||
|
} else {
|
||||||
|
_gfx_debug_flags = strtoul(argv[1], nullptr, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************
|
/*******************************
|
||||||
* console command registration
|
* console command registration
|
||||||
*******************************/
|
*******************************/
|
||||||
@@ -2671,4 +2691,6 @@ void IConsoleStdLibRegister()
|
|||||||
IConsole::CmdRegister("cmstartrecord", citymania::ConStartRecord);
|
IConsole::CmdRegister("cmstartrecord", citymania::ConStartRecord);
|
||||||
IConsole::CmdRegister("cmstoprecord", citymania::ConStopRecord);
|
IConsole::CmdRegister("cmstoprecord", citymania::ConStopRecord);
|
||||||
IConsole::CmdRegister("cmgamestats", citymania::ConGameStats);
|
IConsole::CmdRegister("cmgamestats", citymania::ConGameStats);
|
||||||
|
|
||||||
|
IConsole::CmdRegister("gfx_debug", ConGfxDebug);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,6 +301,30 @@ static inline int DivAwayFromZero(int a, uint b)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a / b rounded towards negative infinity for b > 0.
|
||||||
|
* @param a Numerator
|
||||||
|
* @param b Denominator
|
||||||
|
* @return Quotient, rounded towards negative infinity
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
static inline T DivTowardsNegativeInf(T a, T b)
|
||||||
|
{
|
||||||
|
return (a / b) - (a % b < 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a / b rounded towards positive infinity for b > 0.
|
||||||
|
* @param a Numerator
|
||||||
|
* @param b Denominator
|
||||||
|
* @return Quotient, rounded towards positive infinity
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
static inline T DivTowardsPositiveInf(T a, T b)
|
||||||
|
{
|
||||||
|
return (a / b) + (a % b > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
uint32 IntSqrt(uint32 num);
|
uint32 IntSqrt(uint32 num);
|
||||||
|
|
||||||
#endif /* MATH_FUNC_HPP */
|
#endif /* MATH_FUNC_HPP */
|
||||||
|
|||||||
478
src/gfx.cpp
478
src/gfx.cpp
@@ -21,6 +21,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 "core/backup_type.hpp"
|
#include "core/backup_type.hpp"
|
||||||
#include "viewport_func.h"
|
#include "viewport_func.h"
|
||||||
|
|
||||||
@@ -76,16 +80,23 @@ int _gui_scale_cfg; ///< GUI scale in config.
|
|||||||
*
|
*
|
||||||
* @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;
|
||||||
|
bool _gfx_draw_active = false;
|
||||||
|
static std::vector<Rect> _dirty_blocks;
|
||||||
|
static std::vector<Rect> _pending_dirty_blocks;
|
||||||
|
enum GfxDebugFlags {
|
||||||
|
GDF_SHOW_WINDOW_DIRTY,
|
||||||
|
GDF_SHOW_WIDGET_DIRTY,
|
||||||
|
GDF_SHOW_RECT_DIRTY,
|
||||||
|
};
|
||||||
|
uint32 _gfx_debug_flags;
|
||||||
|
|
||||||
void GfxScroll(int left, int top, int width, int height, int xo, int yo)
|
void GfxScroll(int left, int top, int width, int height, int xo, int yo)
|
||||||
{
|
{
|
||||||
@@ -1510,12 +1521,7 @@ 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));
|
|
||||||
|
|
||||||
/* 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;
|
||||||
@@ -1631,6 +1637,72 @@ 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(const 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DrawOverlappedWindowWithClipping(Window *w, int left, int top, int right, int bottom, DrawOverlappedWindowFlags flags)
|
||||||
|
{
|
||||||
|
extern void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom, DrawOverlappedWindowFlags flags);
|
||||||
|
|
||||||
|
if (right < 0 || bottom < 0 || left >= _screen.width || top >= _screen.height) return;
|
||||||
|
|
||||||
|
DrawOverlappedWindow(w, std::max(0, left), std::max(0, top), std::min(_screen.width, right), std::min(_screen.height, bottom), flags);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repaints the rectangle blocks which are marked as 'dirty'.
|
* Repaints the rectangle blocks which are marked as 'dirty'.
|
||||||
*
|
*
|
||||||
@@ -1640,79 +1712,309 @@ 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;
|
|
||||||
|
|
||||||
y = 0;
|
_gfx_draw_active = true;
|
||||||
do {
|
|
||||||
x = 0;
|
|
||||||
do {
|
|
||||||
if (*b != 0) {
|
|
||||||
int left;
|
|
||||||
int top;
|
|
||||||
int right = x + DIRTY_BLOCK_WIDTH;
|
|
||||||
int bottom = y;
|
|
||||||
byte *p = b;
|
|
||||||
int h2;
|
|
||||||
|
|
||||||
/* First try coalescing downwards */
|
if (_whole_screen_dirty) {
|
||||||
do {
|
RedrawScreenRect(0, 0, _screen.width, _screen.height);
|
||||||
*p = 0;
|
for (Window *w : Window::IterateFromBack()) {
|
||||||
p += _dirty_bytes_per_line;
|
w->flags &= ~(WF_DIRTY | WF_WIDGETS_DIRTY | WF_DRAG_DIRTIED);
|
||||||
bottom += DIRTY_BLOCK_HEIGHT;
|
}
|
||||||
} while (bottom != h && *p != 0);
|
_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;
|
||||||
|
};
|
||||||
|
|
||||||
/* Try coalescing to the right too. */
|
DrawPixelInfo bk;
|
||||||
h2 = (bottom - y) / DIRTY_BLOCK_HEIGHT;
|
Backup dpi_backup(_cur_dpi, &bk, FILE_LINE);
|
||||||
assert(h2 > 0);
|
|
||||||
p = b;
|
|
||||||
|
|
||||||
while (right != w) {
|
for (Window *w : Window::IterateFromBack()) {
|
||||||
byte *p2 = ++p;
|
w->flags &= ~WF_DRAG_DIRTIED;
|
||||||
int h = h2;
|
if (!MayBeShown(w)) continue;
|
||||||
/* Check if a full line of dirty flags is set. */
|
|
||||||
do {
|
|
||||||
if (!*p2) goto no_more_coalesc;
|
|
||||||
p2 += _dirty_bytes_per_line;
|
|
||||||
} while (--h != 0);
|
|
||||||
|
|
||||||
/* Wohoo, can combine it one step to the right!
|
if (w->viewport != nullptr) w->viewport->is_drawn = false;
|
||||||
* Do that, and clear the bits. */
|
|
||||||
right += DIRTY_BLOCK_WIDTH;
|
|
||||||
|
|
||||||
h = h2;
|
if (w->flags & WF_DIRTY) {
|
||||||
p2 = p;
|
clear_overlays();
|
||||||
do {
|
DrawOverlappedWindowFlags flags = DOWF_MARK_DIRTY;
|
||||||
*p2 = 0;
|
if (unlikely(HasBit(_gfx_debug_flags, GDF_SHOW_WINDOW_DIRTY))) {
|
||||||
p2 += _dirty_bytes_per_line;
|
flags |= DOWF_SHOW_DEBUG;
|
||||||
} while (--h != 0);
|
|
||||||
}
|
}
|
||||||
no_more_coalesc:
|
DrawOverlappedWindowWithClipping(w, w->left, w->top, w->left + w->width, w->top + w->height, flags);
|
||||||
|
w->flags &= ~(WF_DIRTY | WF_WIDGETS_DIRTY);
|
||||||
left = x;
|
} else if (w->flags & WF_WIDGETS_DIRTY) {
|
||||||
top = y;
|
if (w->nested_root != nullptr) {
|
||||||
|
clear_overlays();
|
||||||
if (left < _invalid_rect.left ) left = _invalid_rect.left;
|
w->nested_root->FillDirtyWidgets(dirty_widgets);
|
||||||
if (top < _invalid_rect.top ) top = _invalid_rect.top;
|
for (NWidgetBase *widget : dirty_widgets) {
|
||||||
if (right > _invalid_rect.right ) right = _invalid_rect.right;
|
DrawOverlappedWindowFlags flags = DOWF_MARK_DIRTY;
|
||||||
if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
|
if (unlikely(HasBit(_gfx_debug_flags, GDF_SHOW_WIDGET_DIRTY))) {
|
||||||
|
flags |= DOWF_SHOW_DEBUG;
|
||||||
if (left < right && top < bottom) {
|
}
|
||||||
RedrawScreenRect(left, top, right, bottom);
|
DrawOverlappedWindowWithClipping(w, w->left + widget->pos_x, w->top + widget->pos_y, w->left + widget->pos_x + widget->current_x, w->top + widget->pos_y + widget->current_y, flags);
|
||||||
|
}
|
||||||
|
dirty_widgets.clear();
|
||||||
}
|
}
|
||||||
|
w->flags &= ~WF_WIDGETS_DIRTY;
|
||||||
}
|
}
|
||||||
} while (b++, (x += DIRTY_BLOCK_WIDTH) != w);
|
|
||||||
} while (b += -(int)(w / DIRTY_BLOCK_WIDTH) + _dirty_bytes_per_line, (y += DIRTY_BLOCK_HEIGHT) != h);
|
|
||||||
|
|
||||||
|
if (w->viewport != nullptr && !w->IsShaded()) {
|
||||||
|
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();
|
||||||
|
bool skip = true;
|
||||||
|
for (const Window *v : Window::IterateFromBack()) {
|
||||||
|
if (skip) {
|
||||||
|
if (v == w) skip = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
uint y = 0;
|
||||||
|
do {
|
||||||
|
if (vp->dirty_blocks[pos]) {
|
||||||
|
uint left = x;
|
||||||
|
uint top = y;
|
||||||
|
uint right = x + 1;
|
||||||
|
uint bottom = y;
|
||||||
|
uint p = pos;
|
||||||
|
|
||||||
|
/* First try coalescing downwards */
|
||||||
|
do {
|
||||||
|
vp->dirty_blocks[p] = false;
|
||||||
|
p++;
|
||||||
|
bottom++;
|
||||||
|
} while (bottom != grid_h && vp->dirty_blocks[p]);
|
||||||
|
|
||||||
|
/* Try coalescing to the right too. */
|
||||||
|
uint block_h = (bottom - y);
|
||||||
|
p = pos;
|
||||||
|
|
||||||
|
while (right != grid_w) {
|
||||||
|
uint p2 = (p += grid_h);
|
||||||
|
uint check_h = block_h;
|
||||||
|
/* Check if a full line of dirty flags is set. */
|
||||||
|
do {
|
||||||
|
if (!vp->dirty_blocks[p2]) goto no_more_coalesc;
|
||||||
|
p2++;
|
||||||
|
} while (--check_h != 0);
|
||||||
|
|
||||||
|
/* Wohoo, can combine it one step to the right!
|
||||||
|
* Do that, and clear the bits. */
|
||||||
|
right++;
|
||||||
|
|
||||||
|
check_h = block_h;
|
||||||
|
p2 = p;
|
||||||
|
do {
|
||||||
|
vp->dirty_blocks[p2] = false;
|
||||||
|
p2++;
|
||||||
|
} while (--check_h != 0);
|
||||||
|
}
|
||||||
|
no_more_coalesc:
|
||||||
|
|
||||||
|
assert(_cur_dpi == &bk);
|
||||||
|
int draw_left = std::max<int>(0, ((left == 0) ? 0 : vp->dirty_block_left_margin + (left << vp->GetDirtyBlockWidthShift())) + vp->left);
|
||||||
|
int draw_top = std::max<int>(0, (top << vp->GetDirtyBlockHeightShift()) + vp->top);
|
||||||
|
int draw_right = std::min<int>(_screen.width, std::min<int>((right << vp->GetDirtyBlockWidthShift()) + vp->dirty_block_left_margin, vp->width) + vp->left);
|
||||||
|
int draw_bottom = std::min<int>(_screen.height, std::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);
|
||||||
|
|
||||||
|
_transparency_opt = to_backup;
|
||||||
|
w->viewport->ClearDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dpi_backup.Restore();
|
||||||
|
|
||||||
|
for (const Rect &r : _dirty_blocks) {
|
||||||
|
RedrawScreenRect(r.left, r.top, r.right, r.bottom);
|
||||||
|
}
|
||||||
|
if (unlikely(HasBit(_gfx_debug_flags, GDF_SHOW_RECT_DIRTY))) {
|
||||||
|
for (const Rect &r : _dirty_blocks) {
|
||||||
|
GfxFillRect(r.left, r.top, r.right, r.bottom, _string_colourmap[++_dirty_block_colour & 0xF], FILLRECT_CHECKER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_dirty_blocks.clear();
|
||||||
|
while (!_pending_dirty_blocks.empty()) {
|
||||||
|
for (const Rect &r : _pending_dirty_blocks) {
|
||||||
|
AddDirtyBlock(r.left, r.top, r.right, r.bottom);
|
||||||
|
}
|
||||||
|
_pending_dirty_blocks.clear();
|
||||||
|
for (const Rect &r : _dirty_blocks) {
|
||||||
|
RedrawScreenRect(r.left, r.top, r.right, r.bottom);
|
||||||
|
}
|
||||||
|
_dirty_blocks.clear();
|
||||||
|
}
|
||||||
|
_gfx_draw_active = false;
|
||||||
++_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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1729,39 +2031,19 @@ void DrawDirtyBlocks()
|
|||||||
*/
|
*/
|
||||||
void AddDirtyBlock(int left, int top, int right, int bottom)
|
void AddDirtyBlock(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;
|
void AddPendingDirtyBlocks(int left, int top, int right, int bottom)
|
||||||
if (top < _invalid_rect.top ) _invalid_rect.top = top;
|
{
|
||||||
if (right > _invalid_rect.right ) _invalid_rect.right = right;
|
_pending_dirty_blocks.push_back({ left, top, right, bottom });
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1772,7 +2054,7 @@ void AddDirtyBlock(int left, int top, int right, int bottom)
|
|||||||
*/
|
*/
|
||||||
void MarkWholeScreenDirty()
|
void MarkWholeScreenDirty()
|
||||||
{
|
{
|
||||||
AddDirtyBlock(0, 0, _screen.width, _screen.height);
|
_whole_screen_dirty = true;
|
||||||
citymania::SetClientListDirty();
|
citymania::SetClientListDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -161,6 +160,8 @@ const char *GetCharAtPosition(const char *str, int x, FontSize start_fontsize =
|
|||||||
|
|
||||||
void DrawDirtyBlocks();
|
void DrawDirtyBlocks();
|
||||||
void AddDirtyBlock(int left, int top, int right, int bottom);
|
void AddDirtyBlock(int left, int top, int right, int bottom);
|
||||||
|
void AddPendingDirtyBlocks(int left, int top, int right, int bottom);
|
||||||
|
void UnsetDirtyBlocks(int left, int top, int right, int bottom);
|
||||||
void MarkWholeScreenDirty();
|
void MarkWholeScreenDirty();
|
||||||
|
|
||||||
bool CopyPalette(Palette &local_palette, bool force_copy = false);
|
bool CopyPalette(Palette &local_palette, bool force_copy = false);
|
||||||
@@ -182,6 +183,12 @@ static inline int CenterBounds(int min, int max, int size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* window.cpp */
|
/* window.cpp */
|
||||||
|
enum DrawOverlappedWindowFlags {
|
||||||
|
DOWF_NONE = 0,
|
||||||
|
DOWF_MARK_DIRTY = 1 << 0,
|
||||||
|
DOWF_SHOW_DEBUG = 1 << 1,
|
||||||
|
};
|
||||||
|
DECLARE_ENUM_AS_BIT_SET(DrawOverlappedWindowFlags)
|
||||||
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom);
|
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom);
|
||||||
|
|
||||||
void SetMouseCursorBusy(bool busy);
|
void SetMouseCursorBusy(bool busy);
|
||||||
|
|||||||
@@ -133,6 +133,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();
|
||||||
@@ -176,6 +177,7 @@ void FixTitleGameZoom(int zoom_adjust)
|
|||||||
|
|
||||||
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[] = {
|
||||||
@@ -599,5 +601,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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,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
|
||||||
|
|||||||
@@ -1782,6 +1782,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_COLUMN_SPACING = 12;
|
const uint NWidgetNewGRFDisplay::INTER_COLUMN_SPACING = 12;
|
||||||
|
|||||||
@@ -797,6 +797,7 @@ void SetupScreenshotViewport(ScreenshotType t, Viewport *vp, uint32 width, uint3
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UpdateViewportSizeZoom(vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1829,6 +1829,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. */
|
||||||
|
|||||||
@@ -152,7 +152,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)
|
||||||
{
|
{
|
||||||
/* CityMania code begin */
|
/* CityMania code begin */
|
||||||
if (citymania::UseImprovedStationJoin()) {
|
if (citymania::UseImprovedStationJoin()) {
|
||||||
|
|||||||
@@ -26,7 +26,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);
|
||||||
int DrawStationAuthorityText(int left, int right, int top);
|
int DrawStationAuthorityText(int left, int right, int top);
|
||||||
void CheckRedrawStationCoverage(const Window *w);
|
void CheckRedrawStationCoverage(Window *w);
|
||||||
|
|
||||||
using StationPickerCmdProc = std::function<bool(bool test, StationID to_join)>;
|
using StationPickerCmdProc = std::function<bool(bool test, StationID to_join)>;
|
||||||
|
|
||||||
|
|||||||
@@ -1578,6 +1578,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.
|
||||||
|
|||||||
118
src/viewport.cpp
118
src/viewport.cpp
@@ -214,7 +214,7 @@ struct ViewportDrawer {
|
|||||||
citymania::TileHighlight cm_highlight;
|
citymania::TileHighlight cm_highlight;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom);
|
static bool MarkViewportDirty(Viewport *vp, int left, int top, int right, int bottom);
|
||||||
|
|
||||||
static ViewportDrawer _vd;
|
static ViewportDrawer _vd;
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ void DeleteWindowViewport(Window *w)
|
|||||||
if (w->viewport == nullptr) return;
|
if (w->viewport == nullptr) return;
|
||||||
|
|
||||||
delete w->viewport->overlay;
|
delete w->viewport->overlay;
|
||||||
free(w->viewport);
|
delete w->viewport;
|
||||||
w->viewport = nullptr;
|
w->viewport = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,8 +267,9 @@ void InitializeWindowViewport(Window *w, int x, int y,
|
|||||||
{
|
{
|
||||||
assert(w->viewport == nullptr);
|
assert(w->viewport == nullptr);
|
||||||
|
|
||||||
ViewportData *vp = CallocT<ViewportData>(1);
|
ViewportData *vp = new ViewportData();
|
||||||
|
|
||||||
|
vp->overlay = nullptr;
|
||||||
vp->left = x + w->left;
|
vp->left = x + w->left;
|
||||||
vp->top = y + w->top;
|
vp->top = y + w->top;
|
||||||
vp->width = width;
|
vp->width = width;
|
||||||
@@ -276,9 +277,13 @@ void InitializeWindowViewport(Window *w, int x, int y,
|
|||||||
|
|
||||||
vp->zoom = static_cast<ZoomLevel>(Clamp(zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max));
|
vp->zoom = static_cast<ZoomLevel>(Clamp(zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max));
|
||||||
|
|
||||||
|
vp->virtual_left = 0;
|
||||||
|
vp->virtual_top = 0;
|
||||||
vp->virtual_width = ScaleByZoom(width, zoom);
|
vp->virtual_width = ScaleByZoom(width, zoom);
|
||||||
vp->virtual_height = ScaleByZoom(height, zoom);
|
vp->virtual_height = ScaleByZoom(height, zoom);
|
||||||
|
|
||||||
|
UpdateViewportSizeZoom(vp);
|
||||||
|
|
||||||
Point pt;
|
Point pt;
|
||||||
|
|
||||||
if (follow_flags & 0x80000000) {
|
if (follow_flags & 0x80000000) {
|
||||||
@@ -303,8 +308,6 @@ void InitializeWindowViewport(Window *w, int x, int y,
|
|||||||
vp->overlay = nullptr;
|
vp->overlay = nullptr;
|
||||||
|
|
||||||
w->viewport = vp;
|
w->viewport = vp;
|
||||||
vp->virtual_left = 0; // pt.x;
|
|
||||||
vp->virtual_top = 0; // pt.y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Point _vp_move_offs;
|
static Point _vp_move_offs;
|
||||||
@@ -375,6 +378,15 @@ static void DoSetViewportPosition(Window::IteratorToFront it, int left, 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)
|
static void SetViewportPosition(Window *w, int x, int y)
|
||||||
{
|
{
|
||||||
Viewport *vp = w->viewport;
|
Viewport *vp = w->viewport;
|
||||||
@@ -385,6 +397,7 @@ static void SetViewportPosition(Window *w, int x, int y)
|
|||||||
|
|
||||||
vp->virtual_left = x;
|
vp->virtual_left = x;
|
||||||
vp->virtual_top = y;
|
vp->virtual_top = y;
|
||||||
|
UpdateViewportDirtyBlockLeftMargin(vp);
|
||||||
|
|
||||||
/* Viewport is bound to its left top corner, so it must be rounded down (UnScaleByZoomLower)
|
/* Viewport is bound to its left top corner, so it must be rounded down (UnScaleByZoomLower)
|
||||||
* else glitch described in FS#1412 will happen (offset by 1 pixel with zoom level > NORMAL)
|
* else glitch described in FS#1412 will happen (offset by 1 pixel with zoom level > NORMAL)
|
||||||
@@ -1234,8 +1247,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;
|
||||||
|
|
||||||
@@ -1243,7 +1256,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;
|
||||||
@@ -1717,7 +1730,7 @@ static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd)
|
|||||||
/**
|
/**
|
||||||
* Draw/colour the blocks that have been redrawn.
|
* Draw/colour the blocks that have been redrawn.
|
||||||
*/
|
*/
|
||||||
static void ViewportDrawDirtyBlocks()
|
void ViewportDrawDirtyBlocks()
|
||||||
{
|
{
|
||||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||||
const DrawPixelInfo *dpi = _cur_dpi;
|
const DrawPixelInfo *dpi = _cur_dpi;
|
||||||
@@ -1789,8 +1802,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);
|
||||||
|
|
||||||
@@ -1845,7 +1858,16 @@ void ViewportDoDraw(const Viewport *vp, int left, int top, int right, int bottom
|
|||||||
_vd.child_screen_sprites_to_draw.clear();
|
_vd.child_screen_sprites_to_draw.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ViewportDraw(const Viewport *vp, int left, int top, int right, int bottom)
|
void ViewportDrawChk(const Viewport *vp, int left, int top, int right, int bottom) {
|
||||||
|
ViewportDoDraw(vp,
|
||||||
|
ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
|
||||||
|
ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
|
||||||
|
ScaleByZoom(right - vp->left, vp->zoom) + vp->virtual_left,
|
||||||
|
ScaleByZoom(bottom - vp->top, vp->zoom) + vp->virtual_top
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -1859,6 +1881,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;
|
||||||
|
|
||||||
ViewportDoDraw(vp,
|
ViewportDoDraw(vp,
|
||||||
ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
|
ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
|
||||||
ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
|
ScaleByZoom(top - vp->top, vp->zoom) + vp->virtual_top,
|
||||||
@@ -1959,6 +1983,32 @@ 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);
|
||||||
|
|
||||||
|
// if (vp->zoom >= ZOOM_LVL_DRAW_MAP) {
|
||||||
|
// memset(vp->map_draw_vehicles_cache.done_hash_bits, 0, sizeof(vp->map_draw_vehicles_cache.done_hash_bits));
|
||||||
|
// vp->map_draw_vehicles_cache.vehicle_pixels.assign(vp->ScreenArea(), false);
|
||||||
|
|
||||||
|
// if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32) {
|
||||||
|
// vp->land_pixel_cache.assign(vp->ScreenArea() * 4, 0xD7);
|
||||||
|
// } else {
|
||||||
|
// vp->land_pixel_cache.assign(vp->ScreenArea(), 0xD7);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// vp->map_draw_vehicles_cache.vehicle_pixels.clear();
|
||||||
|
// vp->land_pixel_cache.clear();
|
||||||
|
// vp->land_pixel_cache.shrink_to_fit();
|
||||||
|
// }
|
||||||
|
// vp->update_vehicles = true;
|
||||||
|
// FillViewportCoverageRect();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a viewport as dirty for repaint if it displays (a part of) the area the needs to be repainted.
|
* Marks a viewport as dirty for repaint if it displays (a part of) the area the needs to be repainted.
|
||||||
* @param vp The viewport to mark as dirty
|
* @param vp The viewport to mark as dirty
|
||||||
@@ -1969,7 +2019,7 @@ void UpdateViewportPosition(Window *w)
|
|||||||
* @return true if the viewport contains a dirty block
|
* @return true if the viewport contains a dirty block
|
||||||
* @ingroup dirty
|
* @ingroup dirty
|
||||||
*/
|
*/
|
||||||
static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right, int bottom)
|
static bool MarkViewportDirty(Viewport *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;
|
||||||
@@ -1989,12 +2039,40 @@ static bool MarkViewportDirty(const Viewport *vp, int left, int top, int right,
|
|||||||
|
|
||||||
if (top >= vp->virtual_height) return false;
|
if (top >= vp->virtual_height) return false;
|
||||||
|
|
||||||
AddDirtyBlock(
|
uint x = std::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 = (std::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
|
|
||||||
);
|
// TODO somehow not needed in jgrpp
|
||||||
|
assert(x < vp->dirty_blocks_per_row);
|
||||||
|
assert(y < vp->dirty_blocks_per_column);
|
||||||
|
h -= std::max((int)y + (int)h - (int)vp->dirty_blocks_per_column, 0);
|
||||||
|
w -= std::max((int)x + (int)w - (int)vp->dirty_blocks_per_row, 0);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*if (unlikely(vp->zoom >= ZOOM_LVL_DRAW_MAP && !(flags & VMDF_NOT_LANDSCAPE))) {
|
||||||
|
uint l = UnScaleByZoomLower(left, vp->zoom);
|
||||||
|
uint t = UnScaleByZoomLower(top, vp->zoom);
|
||||||
|
uint w = UnScaleByZoom(right, vp->zoom) - l;
|
||||||
|
uint h = UnScaleByZoom(bottom, vp->zoom) - t;
|
||||||
|
uint bitdepth = BlitterFactory::GetCurrentBlitter()->GetScreenDepth() / 8;
|
||||||
|
uint8 *land_cache = vp->land_pixel_cache.data() + ((l + (t * vp->width)) * bitdepth);
|
||||||
|
while (--h) {
|
||||||
|
memset(land_cache, 0xD7, (size_t)w * bitdepth);
|
||||||
|
land_cache += vp->width * bitdepth;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2012,7 +2090,7 @@ bool MarkAllViewportsDirty(int left, int top, int right, int bottom)
|
|||||||
{
|
{
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
|
|
||||||
for (const Window *w : Window::Iterate()) {
|
for (Window *w : Window::Iterate()) {
|
||||||
Viewport *vp = w->viewport;
|
Viewport *vp = w->viewport;
|
||||||
if (vp != nullptr) {
|
if (vp != nullptr) {
|
||||||
assert(vp->width != 0);
|
assert(vp->width != 0);
|
||||||
|
|||||||
@@ -26,6 +26,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);
|
||||||
|
|
||||||
bool MarkAllViewportsDirty(int left, int top, int right, int bottom);
|
bool MarkAllViewportsDirty(int left, int top, int right, int bottom);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +34,35 @@ struct Viewport {
|
|||||||
|
|
||||||
ZoomLevel zoom; ///< The zoom level of the viewport.
|
ZoomLevel zoom; ///< The zoom level of the viewport.
|
||||||
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Location information about a sign as seen on the viewport */
|
/** Location information about a sign as seen on the viewport */
|
||||||
|
|||||||
@@ -1022,11 +1022,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;
|
||||||
AddDirtyBlock(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1217,6 +1216,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.
|
||||||
@@ -1376,6 +1380,8 @@ void NWidgetStacked::FillNestedArray(NWidgetBase **array, uint length)
|
|||||||
|
|
||||||
void NWidgetStacked::Draw(const Window *w)
|
void NWidgetStacked::Draw(const Window *w)
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
@@ -1403,6 +1409,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.
|
||||||
@@ -1447,6 +1468,8 @@ 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;
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@@ -1463,6 +1486,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)
|
||||||
{
|
{
|
||||||
@@ -1834,7 +1868,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. */
|
||||||
}
|
}
|
||||||
@@ -1844,6 +1878,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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -2001,8 +2040,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;
|
||||||
|
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]);
|
||||||
|
|
||||||
@@ -2226,6 +2274,8 @@ void NWidgetBackground::FillNestedArray(NWidgetBase **array, uint length)
|
|||||||
|
|
||||||
void NWidgetBackground::Draw(const Window *w)
|
void NWidgetBackground::Draw(const Window *w)
|
||||||
{
|
{
|
||||||
|
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 = this->GetCurrentRect();
|
Rect r = this->GetCurrentRect();
|
||||||
@@ -2271,6 +2321,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;
|
||||||
@@ -2296,7 +2355,9 @@ void NWidgetViewport::SetupSmallestSize(Window *w, bool init_array)
|
|||||||
|
|
||||||
void NWidgetViewport::Draw(const Window *w)
|
void NWidgetViewport::Draw(const Window *w)
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
|
if (this->IsOutsideDrawArea()) return;
|
||||||
|
|
||||||
if (this->disp_flags & ND_NO_TRANSPARENCY) {
|
if (this->disp_flags & ND_NO_TRANSPARENCY) {
|
||||||
TransparencyOptionBits to_backup = _transparency_opt;
|
TransparencyOptionBits to_backup = _transparency_opt;
|
||||||
@@ -2323,6 +2384,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2340,6 +2402,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2490,6 +2553,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;
|
||||||
|
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 = this->GetCurrentRect();
|
Rect r = this->GetCurrentRect();
|
||||||
@@ -2838,6 +2903,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;
|
||||||
|
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... for WWT_EMPTY or WWT_TEXT, an extra scaled pixel is allowed vertically in case text shadow encroaches. */
|
/* Setup a clipping rectangle... for WWT_EMPTY or WWT_TEXT, an extra scaled pixel is allowed vertically in case text shadow encroaches. */
|
||||||
|
|||||||
@@ -17,6 +17,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. */
|
||||||
@@ -41,7 +43,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
|
||||||
|
|
||||||
@@ -107,6 +109,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.
|
||||||
@@ -170,7 +180,8 @@ 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);
|
||||||
|
|
||||||
Rect GetCurrentRect() const
|
Rect GetCurrentRect() const
|
||||||
{
|
{
|
||||||
@@ -183,6 +194,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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).
|
||||||
@@ -205,6 +217,14 @@ public:
|
|||||||
RectPadding padding; ///< Padding added to the widget. Managed by parent container widget. (parent container may swap left and right for RTL)
|
RectPadding padding; ///< Padding added to the widget. Managed by parent container widget. (parent container may swap left and right for RTL)
|
||||||
RectPadding uz_padding; ///< Unscaled padding, for resize calculation.
|
RectPadding uz_padding; ///< Unscaled padding, for resize calculation.
|
||||||
|
|
||||||
|
inline bool IsOutsideDrawArea() const
|
||||||
|
{
|
||||||
|
extern DrawPixelInfo *_cur_dpi;
|
||||||
|
if ((int)(this->pos_x + this->current_x) <= _cur_dpi->left || (int)(this->pos_x) >= _cur_dpi->left + _cur_dpi->width) return true;
|
||||||
|
if ((int)(this->pos_y + this->current_y) <= _cur_dpi->top || (int)(this->pos_y) >= _cur_dpi->top + _cur_dpi->height) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
inline void StoreSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height);
|
inline void StoreSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height);
|
||||||
};
|
};
|
||||||
@@ -331,6 +351,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.
|
||||||
@@ -451,6 +472,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);
|
||||||
|
|
||||||
@@ -479,6 +501,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.
|
||||||
@@ -549,6 +572,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.
|
||||||
@@ -578,8 +602,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -603,6 +628,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.
|
||||||
|
|||||||
103
src/window.cpp
103
src/window.cpp
@@ -622,7 +622,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;
|
||||||
@@ -889,27 +889,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.
|
||||||
*
|
*
|
||||||
@@ -921,8 +900,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, DrawOverlappedWindowFlags flags)
|
||||||
{
|
{
|
||||||
Window::IteratorToFront it(w);
|
Window::IteratorToFront it(w);
|
||||||
++it;
|
++it;
|
||||||
@@ -937,26 +917,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, flags);
|
||||||
DrawOverlappedWindow(w, x, top, right, bottom);
|
DrawOverlappedWindow(w, x, top, right, bottom, flags);
|
||||||
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, flags);
|
||||||
DrawOverlappedWindow(w, x, top, right, bottom);
|
DrawOverlappedWindow(w, x, top, right, bottom, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top < (x = v->top)) {
|
if (top < (x = v->top)) {
|
||||||
DrawOverlappedWindow(w, left, top, right, x);
|
DrawOverlappedWindow(w, left, top, right, x, flags);
|
||||||
DrawOverlappedWindow(w, left, x, right, bottom);
|
DrawOverlappedWindow(w, left, x, right, bottom, flags);
|
||||||
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, flags);
|
||||||
DrawOverlappedWindow(w, left, x, right, bottom);
|
DrawOverlappedWindow(w, left, x, right, bottom, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -974,6 +954,14 @@ 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 (unlikely(flags & DOWF_SHOW_DEBUG)) {
|
||||||
|
extern void ViewportDrawDirtyBlocks();
|
||||||
|
ViewportDrawDirtyBlocks();
|
||||||
|
}
|
||||||
|
if (flags & DOWF_MARK_DIRTY) {
|
||||||
|
VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top);
|
||||||
|
UnsetDirtyBlocks(left, top, right, bottom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -997,19 +985,34 @@ 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, std::max(left, w->left), std::max(top, w->top), std::min(right, w->left + w->width), std::min(bottom, w->top + w->height));
|
DrawOverlappedWindow(w, std::max(left, w->left), std::max(top, w->top), std::min(right, w->left + w->width), std::min(bottom, w->top + w->height), DOWF_NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_cur_dpi = old_dpi;
|
_cur_dpi = old_dpi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark entire window as dirty (in need of re-paint)
|
||||||
|
* @ingroup dirty
|
||||||
|
*/
|
||||||
|
void Window::SetDirty()
|
||||||
|
{
|
||||||
|
this->flags |= WF_DIRTY;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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::SetDirtyAsBlocks()
|
||||||
{
|
{
|
||||||
AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
|
extern bool _gfx_draw_active;
|
||||||
|
if (_gfx_draw_active) {
|
||||||
|
AddPendingDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
|
||||||
|
} else {
|
||||||
|
AddDirtyBlock(this->left, this->top, this->left + this->width, this->top + this->height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1020,7 +1023,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;
|
||||||
@@ -1135,7 +1138,7 @@ void Window::Close()
|
|||||||
|
|
||||||
this->CloseChildWindows();
|
this->CloseChildWindows();
|
||||||
|
|
||||||
this->SetDirty();
|
this->SetDirtyAsBlocks();
|
||||||
|
|
||||||
Window::closed_windows.push_back(this);
|
Window::closed_windows.push_back(this);
|
||||||
}
|
}
|
||||||
@@ -2102,7 +2105,7 @@ void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
|
|||||||
if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
|
if (new_bottom >= (int)_screen.height) delta_y -= Ceil(new_bottom - _screen.height, std::max(1U, w->nested_root->resize_y));
|
||||||
}
|
}
|
||||||
|
|
||||||
w->SetDirty();
|
w->SetDirtyAsBlocks();
|
||||||
|
|
||||||
uint new_xinc = std::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 = std::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 = std::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 = std::max(0, (w->nested_root->resize_y == 0) ? 0 : (int)(w->nested_root->current_y - w->nested_root->smallest_y) + delta_y);
|
||||||
@@ -2118,7 +2121,14 @@ void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
|
|||||||
|
|
||||||
/* Always call OnResize to make sure everything is initialised correctly if it needs to be. */
|
/* Always call OnResize to make sure everything is initialised correctly if it needs to be. */
|
||||||
w->OnResize();
|
w->OnResize();
|
||||||
w->SetDirty();
|
|
||||||
|
// TODO incapsulate into a function
|
||||||
|
extern bool _gfx_draw_active;
|
||||||
|
if (_gfx_draw_active) {
|
||||||
|
AddPendingDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height);
|
||||||
|
} else {
|
||||||
|
w->SetDirty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2166,7 +2176,10 @@ static EventState HandleWindowDragging()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
w->SetDirty();
|
if (!(w->flags & WF_DRAG_DIRTIED)) {
|
||||||
|
w->flags |= WF_DRAG_DIRTIED;
|
||||||
|
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;
|
||||||
@@ -2299,7 +2312,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 {
|
||||||
@@ -3164,7 +3177,7 @@ void UpdateWindows()
|
|||||||
*/
|
*/
|
||||||
void SetWindowDirty(WindowClass cls, WindowNumber number)
|
void SetWindowDirty(WindowClass cls, WindowNumber number)
|
||||||
{
|
{
|
||||||
for (const Window *w : Window::Iterate()) {
|
for (Window *w : Window::Iterate()) {
|
||||||
if (w->window_class == cls && w->window_number == number) w->SetDirty();
|
if (w->window_class == cls && w->window_number == number) w->SetDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3177,7 +3190,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)
|
||||||
{
|
{
|
||||||
for (const Window *w : Window::Iterate()) {
|
for (Window *w : Window::Iterate()) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -3190,7 +3203,7 @@ void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_inde
|
|||||||
*/
|
*/
|
||||||
void SetWindowClassesDirty(WindowClass cls)
|
void SetWindowClassesDirty(WindowClass cls)
|
||||||
{
|
{
|
||||||
for (const Window *w : Window::Iterate()) {
|
for (Window *w : Window::Iterate()) {
|
||||||
if (w->window_class == cls) w->SetDirty();
|
if (w->window_class == cls) w->SetDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3362,7 +3375,7 @@ void CloseConstructionWindows()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const Window *w : Window::Iterate()) w->SetDirty();
|
for (Window *w : Window::Iterate()) w->SetDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Close all always on-top windows to get an empty screen */
|
/** Close all always on-top windows to get an empty screen */
|
||||||
@@ -3486,7 +3499,7 @@ int PositionNetworkChatWindow(Window *w)
|
|||||||
*/
|
*/
|
||||||
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
|
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
|
||||||
{
|
{
|
||||||
for (const Window *w : Window::Iterate()) {
|
for (Window *w : Window::Iterate()) {
|
||||||
if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
|
if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) {
|
||||||
w->viewport->follow_vehicle = to_index;
|
w->viewport->follow_vehicle = to_index;
|
||||||
w->SetDirty();
|
w->SetDirty();
|
||||||
|
|||||||
@@ -165,7 +165,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.
|
||||||
@@ -177,6 +177,9 @@ 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.
|
||||||
|
WF_DRAG_DIRTIED = 1 << 13, ///< The window has already been marked dirty as blocks as part of the current drag operation
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_AS_BIT_SET(WindowFlags)
|
DECLARE_ENUM_AS_BIT_SET(WindowFlags)
|
||||||
|
|
||||||
@@ -254,7 +257,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.
|
||||||
@@ -438,7 +442,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;
|
||||||
@@ -449,7 +453,8 @@ public:
|
|||||||
virtual void Close();
|
virtual void Close();
|
||||||
static void DeleteClosedWindows();
|
static void DeleteClosedWindows();
|
||||||
|
|
||||||
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? */
|
||||||
@@ -961,4 +966,26 @@ private:
|
|||||||
WindowPopupType type;
|
WindowPopupType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user