GUI: Remove the ThemeItem draw queues

Drawing nows happens directly when the Dialog or Widget draw methods are
called. This makes it easy to debug why a particular low level draw
method was called, by inspecting the call stack.

This replaces the notion of "buffering" by two independant ways to
control what is drawn and where:
- The active layer is used to select whether the foreground or
  background part of the dialogs are rendered by the draw calls.
- The active surface is used to select if the draw calls affect the back
  buffer or the screen.

The foreground layer of the active dialog is drawn directly to the
screen. Its background layer is drawn to the back buffer. This way
widgets can restore the back buffer in order to update without having to
redraw the dialog's background.

Dialogs lower in the dialog stack are drawn entirely to the back buffer.
This commit is contained in:
Bastien Bouclet 2018-01-07 10:39:22 +01:00 committed by Eugene Sandulenko
parent 99eb0159db
commit 4d0bb753e4
17 changed files with 374 additions and 533 deletions

View file

@ -514,9 +514,10 @@ ValueDisplayDialog::ValueDisplayDialog(const Common::String& label, int minVal,
assert(_min <= _value && _value <= _max);
}
void ValueDisplayDialog::drawDialog() {
void ValueDisplayDialog::drawDialog(GUI::DrawLayer layerToDraw) {
Dialog::drawDialog(layerToDraw);
const int labelWidth = _w - 8 - _percentBarWidth;
g_gui.theme()->drawDialogBackground(Common::Rect(_x, _y, _x+_w, _y+_h), GUI::ThemeEngine::kDialogBackgroundDefault);
g_gui.theme()->drawText(Common::Rect(_x+4, _y+4, _x+labelWidth+4,
_y+g_gui.theme()->getFontHeight()+4), _label);
g_gui.theme()->drawSlider(Common::Rect(_x+4+labelWidth, _y+4, _x+_w-4, _y+_h-4),

View file

@ -128,7 +128,7 @@ public:
ValueDisplayDialog(const Common::String& label, int minVal, int maxVal, int val, uint16 incKey, uint16 decKey);
virtual void open();
virtual void drawDialog();
void drawDialog(GUI::DrawLayer layerToDraw) override;
virtual void handleTickle();
virtual void handleMouseDown(int x, int y, int button, int clickCount) {
close();

View file

@ -289,6 +289,13 @@ public:
_activeSurface = surface;
}
/**
* Returns the currently active drawing surface
*/
virtual TransparentSurface *getActiveSurface() {
return _activeSurface;
}
/**
* Fills the active surface with the specified fg/bg color or the active gradient.
* Defaults to using the active Foreground color for filling.

View file

@ -557,17 +557,19 @@ Common::SaveFileManager *EventRecorder::getSaveManager(Common::SaveFileManager *
}
void EventRecorder::preDrawOverlayGui() {
if ((_initialized) || (_needRedraw)) {
if ((_initialized) || (_needRedraw)) {
RecordMode oldMode = _recordMode;
_recordMode = kPassthrough;
g_system->showOverlay();
g_gui.theme()->clearAll();
g_gui.theme()->openDialog(true, GUI::ThemeEngine::kShadingNone);
_controlPanel->drawDialog();
g_gui.theme()->finishBuffering();
g_gui.theme()->drawToBackbuffer();
_controlPanel->drawDialog(kDrawLayerBackground);
g_gui.theme()->drawToScreen();
g_gui.theme()->copyBackBufferToScreen();
_controlPanel->drawDialog(kDrawLayerForeground);
g_gui.theme()->updateScreen();
_recordMode = oldMode;
}
}
}
void EventRecorder::postDrawOverlayGui() {

File diff suppressed because it is too large Load diff

View file

@ -54,7 +54,6 @@ struct TextColorData;
class Dialog;
class GuiObject;
class ThemeEval;
class ThemeItem;
class ThemeParser;
/**
@ -113,6 +112,18 @@ enum DrawData {
kDDNone = -1
};
/**
* Dialog layers.
* The currently active dialog has two layers, background and foreground.
* The background layer is drawn to the backbuffer. The foreground layer
* is drawn to the screen. This allows draw calls to restore the background
* layer before redrawing a widget.
*/
enum DrawLayer {
kDrawLayerBackground,
kDrawLayerForeground
};
// FIXME: TextData is really a bad name, not conveying what this enum is about.
enum TextData {
kTextDataNone = -1,
@ -308,22 +319,38 @@ public:
const Graphics::PixelFormat getPixelFormat() const { return _overlayFormat; }
/**
* Implementation of the GUI::Theme API. Called when a
* new dialog is opened. Note that the boolean parameter
* meaning has been changed.
* Draw full screen shading with the supplied style
*
* @param enableBuffering If set to true, buffering is enabled for
* drawing this dialog, and will continue enabled
* until disabled.
* This is used to dim the inactive dialogs so the active one stands out.
*/
void openDialog(bool enableBuffering, ShadingStyle shading = kShadingNone);
void applyScreenShading(ShadingStyle shading);
/**
* Sets the active drawing surface to the back buffer.
*
* All drawing from this point on will be done on that surface.
* The back buffer surface needs to be copied to the screen surface
* in order to become visible.
*/
void drawToBackbuffer();
/**
* Sets the active drawing surface to the screen.
*
* All drawing from this point on will be done on that surface.
*/
void drawToScreen();
/**
* The updateScreen() method is called every frame.
* It processes all the drawing queues and then copies dirty rects
* in the current Screen surface to the overlay.
* It copies dirty rectangles in the Screen surface to the overlay.
*/
void updateScreen(bool render = true);
void updateScreen();
/**
* Copy the entire backbuffer surface to the screen surface
*/
void copyBackBufferToScreen();
/** @name FONT MANAGEMENT METHODS */
@ -425,8 +452,8 @@ public:
/**
* Actual implementation of a dirty rect handling.
* Dirty rectangles are queued on a list and are later used for the
* actual drawing.
* Dirty rectangles are queued on a list, merged and optimized
* when possible and are later used for the actual drawing.
*
* @param r Area of the dirty rect.
*/
@ -534,13 +561,6 @@ protected:
void setGraphicsMode(GraphicsMode mode);
public:
/**
* Finishes buffering: widgets from then on will be drawn straight on the screen
* without drawing queues.
*/
inline void finishBuffering() { _buffering = false; }
inline void startBuffering() { _buffering = true; }
inline ThemeEval *getEvaluator() { return _themeEval; }
inline Graphics::VectorRenderer *renderer() { return _vectorRenderer; }
@ -623,33 +643,35 @@ protected:
const Graphics::Font *loadFont(const Common::String &filename, const Common::String &scalableFilename, const Common::String &charset, const int pointsize, const bool makeLocalizedFont);
/**
* Actual Dirty Screen handling function.
* Handles all the dirty squares in the list, merges and optimizes
* them when possible and draws them to the screen.
* Called from updateScreen()
* Dirty Screen handling function.
* Draws all the dirty rectangles in the list to the overlay.
*/
void renderDirtyScreen();
void updateDirtyScreen();
/**
* Generates a DrawQueue item and enqueues it so it's drawn to the screen
* when the drawing queue is processed.
* Draws a GUI element according to a DrawData descriptor.
*
* If Buffering is enabled, the DrawQueue item will be automatically placed
* on its corresponding queue.
* If Buffering is disabled, the DrawQueue item will be processed immediately
* and drawn to the screen.
* Only calls with a DrawData layer attribute matching the active layer
* are actually drawn to the active surface.
*
* This function is called from all the Widget Drawing methods.
* These functions are called from all the Widget drawing methods.
*/
void queueDD(DrawData type, const Common::Rect &r, uint32 dynamic = 0, bool restore = false);
void queueDDClip(DrawData type, const Common::Rect &r, const Common::Rect &clippingRect, uint32 dynamic = 0, bool restore = false);
void queueDDText(TextData type, TextColor color, const Common::Rect &r, const Common::String &text, bool restoreBg,
bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft, TextAlignVertical alignV = kTextAlignVTop, int deltax = 0, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
void queueDDTextClip(TextData type, TextColor color, const Common::Rect &r, const Common::Rect &clippingRect, const Common::String &text, bool restoreBg,
bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft, TextAlignVertical alignV = kTextAlignVTop, int deltax = 0, const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
void queueBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha);
void queueBitmapClip(const Graphics::Surface *bitmap, const Common::Rect &clippingRect, const Common::Rect &r, bool alpha);
void queueABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, AutoScaleMode autoscale, int alpha);
void drawDD(DrawData type, const Common::Rect &r, uint32 dynamic = 0, bool forceRestore = false);
void drawDDClip(DrawData type, const Common::Rect &r, const Common::Rect &clippingRect, uint32 dynamic = 0,
bool forceRestore = false);
void drawDDText(TextData type, TextColor color, const Common::Rect &r, const Common::String &text, bool restoreBg,
bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft,
TextAlignVertical alignV = kTextAlignVTop, int deltax = 0,
const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
void drawDDTextClip(TextData type, TextColor color, const Common::Rect &r, const Common::Rect &clippingRect,
const Common::String &text, bool restoreBg,
bool elipsis, Graphics::TextAlign alignH = Graphics::kTextAlignLeft,
TextAlignVertical alignV = kTextAlignVTop, int deltax = 0,
const Common::Rect &drawableTextArea = Common::Rect(0, 0, 0, 0));
void drawBitmap(const Graphics::Surface *bitmap, const Common::Rect &r, bool alpha);
void drawBitmapClip(const Graphics::Surface *bitmap, const Common::Rect &clippingRect, const Common::Rect &r,
bool alpha);
void drawABitmap(Graphics::TransparentSurface *bitmap, const Common::Rect &r, AutoScaleMode autoscale, int alpha);
/**
* DEBUG: Draws a white square and writes some text next to it.
@ -695,9 +717,13 @@ protected:
/** Backbuffer surface. Stores previous states of the screen to blit back */
Graphics::TransparentSurface _backBuffer;
/** Sets whether the current drawing is being buffered (stored for later
processing) or drawn directly to the screen. */
bool _buffering;
/**
* Filter the submitted DrawData descriptors according to their layer attribute
*
* This is used to selectively draw the background or foreground layer
* of the dialogs.
*/
DrawLayer _layerToDraw;
/** Bytes per pixel of the Active Drawing Surface (i.e. the screen) */
int _bytesPerPixel;
@ -731,12 +757,6 @@ protected:
/** List of all the dirty screens that must be blitted to the overlay. */
Common::List<Common::Rect> _dirtyScreen;
/** Queue with all the drawing that must be done to the Back Buffer */
Common::List<ThemeItem *> _bufferQueue;
/** Queue with all the drawing that must be done to the screen */
Common::List<ThemeItem *> _screenQueue;
bool _initOk; ///< Class and renderer properly initialized
bool _themeOk; ///< Theme data successfully loaded.
bool _enabled; ///< Whether the Theme is currently shown on the overlay

View file

@ -56,11 +56,11 @@ void Tooltip::setup(Dialog *parent, Widget *widget, int x, int y) {
_y = MIN<int16>(parent->_y + y + _ydelta, g_gui.getHeight() - _h - 3);
}
void Tooltip::drawDialog() {
void Tooltip::drawDialog(DrawLayer layerToDraw) {
int num = 0;
int h = g_gui.theme()->getFontHeight(ThemeEngine::kFontStyleTooltip) + 2;
Dialog::drawDialog();
Dialog::drawDialog(layerToDraw);
for (Common::StringArray::const_iterator i = _wrappedLines.begin(); i != _wrappedLines.end(); ++i, ++num) {
g_gui.theme()->drawText(

View file

@ -40,7 +40,7 @@ public:
void setup(Dialog *parent, Widget *widget, int x, int y);
void drawDialog();
void drawDialog(DrawLayer layerToDraw) override;
virtual void receivedFocus(int x = -1, int y = -1) {}
protected:

View file

@ -180,8 +180,8 @@ void AboutDialog::close() {
Dialog::close();
}
void AboutDialog::drawDialog() {
Dialog::drawDialog();
void AboutDialog::drawDialog(DrawLayer layerToDraw) {
Dialog::drawDialog(layerToDraw);
setTextDrawableArea(Common::Rect(_x, _y, _x + _w, _y + _h));
@ -268,7 +268,7 @@ void AboutDialog::handleTickle() {
_scrollPos = 0;
_scrollTime += kScrollStartDelay;
}
drawDialog();
drawDialog(kDrawLayerForeground);
}
}

View file

@ -48,7 +48,7 @@ public:
void open();
void close();
void drawDialog();
void drawDialog(DrawLayer layerToDraw) override;
void handleTickle();
void handleMouseUp(int x, int y, int button, int clickCount);
void handleKeyDown(Common::KeyState state);

View file

@ -163,18 +163,11 @@ void ConsoleDialog::close() {
Dialog::close();
}
void ConsoleDialog::drawDialog() {
g_gui.theme()->drawDialogBackground(Common::Rect(_x, _y, _x + _w, _y + _h), ThemeEngine::kDialogBackgroundPlain/*_backgroundType*/);
// FIXME: for the old theme the frame around the console vanishes
// when any action is processed if we enable this
// _drawingHints &= ~THEME_HINT_FIRST_DRAW;
void ConsoleDialog::drawDialog(DrawLayer layerToDraw) {
Dialog::drawDialog(layerToDraw);
for (int line = 0; line < _linesPerPage; line++)
drawLine(line, false);
// Draw the scrollbar
_scrollBar->markAsDirty();
_scrollBar->draw();
}
void ConsoleDialog::drawLine(int line, bool restoreBg) {
@ -188,7 +181,6 @@ void ConsoleDialog::drawLine(int line, bool restoreBg) {
if (restoreBg) {
Common::Rect r(_x, y - 2, _x + _pageWidth * kConsoleCharWidth, y+kConsoleLineHeight);
g_gui.theme()->restoreBackground(r);
g_gui.theme()->addDirtyRect(r);
}
for (int column = 0; column < limit; column++) {
@ -201,8 +193,6 @@ void ConsoleDialog::drawLine(int line, bool restoreBg) {
g_gui.theme()->drawChar(Common::Rect(x, y, x+kConsoleCharWidth, y+kConsoleLineHeight), c, _font);
x += kConsoleCharWidth;
}
g_gui.theme()->updateScreen();
}
void ConsoleDialog::reflowLayout() {

View file

@ -132,7 +132,7 @@ public:
void open();
void close();
void drawDialog();
void drawDialog(DrawLayer layerToDraw) override;
void handleTickle();
void reflowLayout();

View file

@ -161,11 +161,12 @@ void Dialog::markWidgetsAsDirty() {
}
}
void Dialog::drawDialog() {
void Dialog::drawDialog(DrawLayer layerToDraw) {
if (!isVisible())
return;
g_gui.theme()->_layerToDraw = layerToDraw;
g_gui.theme()->drawDialogBackground(Common::Rect(_x, _y, _x+_w, _y+_h), _backgroundType);
markWidgetsAsDirty();

View file

@ -92,7 +92,7 @@ protected:
void markWidgetsAsDirty();
/** Draw the dialog in its entirety (background and widgets) */
virtual void drawDialog();
virtual void drawDialog(DrawLayer layerToDraw);
/** Draw only the dialog's widgets */
void drawWidgets();

View file

@ -224,20 +224,33 @@ void GuiManager::redraw() {
case kRedrawFull:
case kRedrawTopDialog:
_theme->clearAll();
_theme->openDialog(true, ThemeEngine::kShadingNone);
_theme->drawToBackbuffer();
for (DialogStack::size_type i = 0; i < _dialogStack.size() - 1; i++)
_dialogStack[i]->drawDialog();
_theme->finishBuffering();
for (DialogStack::size_type i = 0; i < _dialogStack.size() - 1; i++) {
_dialogStack[i]->drawDialog(kDrawLayerBackground);
_dialogStack[i]->drawDialog(kDrawLayerForeground);
}
// fall through
case kRedrawOpenDialog:
_theme->updateScreen(false);
_theme->openDialog(true, shading);
_dialogStack.top()->drawDialog();
_theme->finishBuffering();
// This case is an optimization to avoid redrawing the whole dialog
// stack when opening a new dialog.
_theme->drawToBackbuffer();
if (_redrawStatus == kRedrawOpenDialog && _dialogStack.size() > 1) {
Dialog *previousDialog = _dialogStack[_dialogStack.size() - 2];
previousDialog->drawDialog(kDrawLayerForeground);
}
_theme->applyScreenShading(shading);
_dialogStack.top()->drawDialog(kDrawLayerBackground);
_theme->drawToScreen();
_theme->copyBackBufferToScreen();
_dialogStack.top()->drawDialog(kDrawLayerForeground);
break;
default:
@ -245,6 +258,7 @@ void GuiManager::redraw() {
}
// Redraw the widgets that are marked as dirty
_theme->drawToScreen();
_dialogStack.top()->drawWidgets();
_theme->updateScreen();

View file

@ -274,7 +274,7 @@ void MassAddDialog::handleTickle() {
_list->scrollToEnd();
}
drawDialog();
drawDialog(kDrawLayerForeground);
}

View file

@ -37,7 +37,6 @@ class PopUpDialog : public Dialog {
protected:
PopUpWidget *_popUpBoss;
int _clickX, _clickY;
byte *_buffer;
int _selection;
uint32 _openTime;
bool _twoColumns;
@ -49,7 +48,7 @@ protected:
public:
PopUpDialog(PopUpWidget *boss, int clickX, int clickY);
void drawDialog();
void drawDialog(DrawLayer layerToDraw) override;
void handleMouseUp(int x, int y, int button, int clickCount);
void handleMouseWheel(int x, int y, int direction); // Scroll through entries with scroll wheel
@ -70,9 +69,9 @@ protected:
PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY)
: Dialog(0, 0, 16, 16),
_popUpBoss(boss) {
_backgroundType = ThemeEngine::kDialogBackgroundNone;
_openTime = 0;
_buffer = nullptr;
_entriesPerColumn = 1;
// Copy the selection index
@ -148,7 +147,9 @@ PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY)
_clickY = clickY - _y;
}
void PopUpDialog::drawDialog() {
void PopUpDialog::drawDialog(DrawLayer layerToDraw) {
Dialog::drawDialog(layerToDraw);
// Draw the menu border
g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), 0);