From 4910d1392c679b5344fbeefcdc532f969565efb8 Mon Sep 17 00:00:00 2001 From: Charles Dang Date: Fri, 2 Jun 2017 14:07:27 +1100 Subject: [PATCH] GUI2: refactored viewport setting and re-added clip rect setting It turns out I had removed the clip rect setting in error; it prevented items in, say, a listbox from drawing outside the widget's boundaries. I've re-added that now. I've also moved viewport setting to the same place. It turns out it didn't need to be set every canvas draw. The only reason the old code was passing the blitting rect to the canvas was it was needed for the sdl_blit call. Since I can now set the viewport independent of a canvas state, there's no longer a need to set the viewport in the canvas. --- src/gui/core/canvas.cpp | 19 ++++++---- src/gui/core/canvas.hpp | 8 ++-- src/gui/widgets/panel.cpp | 8 ++-- src/gui/widgets/styled_widget.cpp | 4 +- src/gui/widgets/widget.cpp | 62 +++++++++++++++++++++++++++++-- src/gui/widgets/widget.hpp | 6 +-- 6 files changed, 81 insertions(+), 26 deletions(-) diff --git a/src/gui/core/canvas.cpp b/src/gui/core/canvas.cpp index 1856f29d4afb..cc29d7cc9808 100644 --- a/src/gui/core/canvas.cpp +++ b/src/gui/core/canvas.cpp @@ -1474,17 +1474,19 @@ void canvas::draw(const bool force) is_dirty_ = false; } -void canvas::render(SDL_Rect rect) +void canvas::render() { - SDL_RenderSetViewport(renderer_, &rect); + /** + * @note Both the clip rect and viewport should be set before this function is called. + * The clip rect ensures the canvas texture is cropped appropriately, and the viewport sets the + * origin for all the drawing operations, as well as specifying the area of the screen to which + * this canvas applies. + */ + // Update the canvas texture, if necessary. draw(); - SDL_RenderCopy(renderer_, texture_, nullptr, nullptr); - - SDL_RenderSetViewport(renderer_, nullptr); - - // TODO: reenable + // TODO: reenable. Need a shader. #if 0 if(blur_depth_) { /* @@ -1503,6 +1505,9 @@ void canvas::render(SDL_Rect rect) } } #endif + + // Copy the entire texture to the full viewport. + SDL_RenderCopy(renderer_, texture_, nullptr, nullptr); } void canvas::parse_cfg(const config& cfg) diff --git a/src/gui/core/canvas.hpp b/src/gui/core/canvas.hpp index ff93a304edea..b312c0ed1fe9 100644 --- a/src/gui/core/canvas.hpp +++ b/src/gui/core/canvas.hpp @@ -104,12 +104,10 @@ class canvas /** * Copies the canvas texture to the screen renderer. * - * It makes sure the image on the canvas is up to date. Also executes the - * pre-blitting functions. - * - * @param rect The place to blit to. + * This will re-render the canvas texture if necessary (ie, if marked dirty). + * It also executes the pre-commit functions such as blurring (@todo: reenable). */ - void render(SDL_Rect rect); + void render(); /** * Sets the config. diff --git a/src/gui/widgets/panel.cpp b/src/gui/widgets/panel.cpp index 60b6a1216f84..6ff261338483 100644 --- a/src/gui/widgets/panel.cpp +++ b/src/gui/widgets/panel.cpp @@ -64,18 +64,18 @@ unsigned panel::get_state() const return 0; } -void panel::impl_draw_background(int x_offset, int y_offset) +void panel::impl_draw_background(int /*x_offset*/, int /*y_offset*/) { DBG_GUI_D << LOG_HEADER << " size " << get_rectangle() << ".\n"; - get_canvas(0).render(calculate_blitting_rectangle(x_offset, y_offset)); + get_canvas(0).render(); } -void panel::impl_draw_foreground(int x_offset, int y_offset) +void panel::impl_draw_foreground(int /*x_offset*/, int /*y_offset*/) { DBG_GUI_D << LOG_HEADER << " size " << get_rectangle() << ".\n"; - get_canvas(1).render(calculate_blitting_rectangle(x_offset, y_offset)); + get_canvas(1).render(); } point panel::border_space() const diff --git a/src/gui/widgets/styled_widget.cpp b/src/gui/widgets/styled_widget.cpp index 41d414b5dc14..ac86ec0c6184 100644 --- a/src/gui/widgets/styled_widget.cpp +++ b/src/gui/widgets/styled_widget.cpp @@ -408,12 +408,12 @@ int styled_widget::get_text_maximum_height() const return get_height() - config_->text_extra_height; } -void styled_widget::impl_draw_background(int x_offset, int y_offset) +void styled_widget::impl_draw_background(int /*x_offset*/, int /*y_offset*/) { DBG_GUI_D << LOG_HEADER << " label '" << debug_truncate(label_) << "' size " << get_rectangle() << ".\n"; - get_canvas(get_state()).render(calculate_blitting_rectangle(x_offset, y_offset)); + get_canvas(get_state()).render(); } void styled_widget::impl_draw_foreground(int /*x_offset*/, int /*y_offset*/) diff --git a/src/gui/widgets/widget.cpp b/src/gui/widgets/widget.cpp index 3726282f2e84..0241df8b7748 100644 --- a/src/gui/widgets/widget.cpp +++ b/src/gui/widgets/widget.cpp @@ -21,6 +21,7 @@ #include "gui/core/log.hpp" #include "gui/core/window_builder/helper.hpp" #include "sdl/rect.hpp" +#include "video.hpp" namespace gui2 { @@ -346,8 +347,7 @@ void widget::set_linked_group(const std::string& linked_group) /***** ***** ***** ***** Drawing functions. ***** ***** ***** *****/ -SDL_Rect widget::calculate_blitting_rectangle(const int x_offset, - const int y_offset) +SDL_Rect widget::calculate_blitting_rectangle(const int x_offset, const int y_offset) const { SDL_Rect result = get_rectangle(); result.x += x_offset; @@ -355,8 +355,7 @@ SDL_Rect widget::calculate_blitting_rectangle(const int x_offset, return result; } -SDL_Rect widget::calculate_clipping_rectangle(const int x_offset, - const int y_offset) +SDL_Rect widget::calculate_clipping_rectangle(const int x_offset, const int y_offset) const { SDL_Rect result = clipping_rectangle_; result.x += x_offset; @@ -364,10 +363,61 @@ SDL_Rect widget::calculate_clipping_rectangle(const int x_offset, return result; } +namespace +{ +/** + * Small RAII helper class to set the renderer viewport and clip rect for the drawing routines. + */ +class viewport_and_clip_rect_setter +{ +public: + viewport_and_clip_rect_setter(const widget& widget, int x_offset, int y_offset) + : renderer_(*CVideo::get_singleton().get_window()) + { + // Set viewport. + const SDL_Rect dst_rect = widget.calculate_blitting_rectangle(x_offset, y_offset); + SDL_RenderSetViewport(renderer_, &dst_rect); + + // Set clip rect, if appropriate. + if(widget.get_drawing_action() != widget::redraw_action::partly) { + return; + } + + SDL_Rect clip_rect = widget.calculate_clipping_rectangle(x_offset, y_offset); + + // Adjust clip rect origin to match the viewport origin. Currently, the both rects are mapped to + // absolute screen coordinates. However, setting the viewport essentially moves the screen origin, + // meaning if both the viewport rect and clip rect have x = 100, then clipping will actually + // happen at x = 200. + clip_rect.x -= dst_rect.x; + clip_rect.y -= dst_rect.y; + + SDL_RenderSetClipRect(renderer_, &clip_rect); + } + + ~viewport_and_clip_rect_setter() + { + SDL_RenderSetClipRect(renderer_, nullptr); + SDL_RenderSetViewport(renderer_, nullptr); + } + +private: + SDL_Renderer* renderer_; +}; + +} // anon namespace + +/** + * @todo remove the offset arguments from these functions. + * Currently they're only needed by the minimap. + */ + void widget::draw_background(int x_offset, int y_offset) { assert(visible_ == visibility::visible); + viewport_and_clip_rect_setter setter(*this, x_offset, y_offset); + draw_debug_border(x_offset, y_offset); impl_draw_background(x_offset, y_offset); } @@ -376,6 +426,8 @@ void widget::draw_children(int x_offset, int y_offset) { assert(visible_ == visibility::visible); + viewport_and_clip_rect_setter setter(*this, x_offset, y_offset); + impl_draw_children(x_offset, y_offset); } @@ -383,6 +435,8 @@ void widget::draw_foreground(int x_offset, int y_offset) { assert(visible_ == visibility::visible); + viewport_and_clip_rect_setter setter(*this, x_offset, y_offset); + impl_draw_foreground(x_offset, y_offset); } diff --git a/src/gui/widgets/widget.hpp b/src/gui/widgets/widget.hpp index 9a9859b3fa5a..a6cabc3e8614 100644 --- a/src/gui/widgets/widget.hpp +++ b/src/gui/widgets/widget.hpp @@ -530,8 +530,7 @@ class widget : public event_executor, public event::dispatcher * * @returns The drawing rectangle. */ - SDL_Rect calculate_blitting_rectangle(const int x_offset, - const int y_offset); + SDL_Rect calculate_blitting_rectangle(const int x_offset, const int y_offset) const; /** * Calculates the clipping rectangle of the widget. @@ -545,8 +544,7 @@ class widget : public event_executor, public event::dispatcher * * @returns The clipping rectangle. */ - SDL_Rect calculate_clipping_rectangle(const int x_offset, - const int y_offset); + SDL_Rect calculate_clipping_rectangle(const int x_offset, const int y_offset) const; /** * Draws the background of a widget.