The Battle for Wesnoth  1.17.21+dev
show_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
18 #include "show_dialog.hpp"
19 
20 #include "draw.hpp"
21 #include "draw_manager.hpp"
22 #include "floating_label.hpp"
23 #include "picture.hpp"
24 #include "gettext.hpp"
25 #include "gui/core/event/handler.hpp" // is_in_dialog
26 #include "help/help.hpp"
28 #include "log.hpp"
29 #include "font/sdl_ttf_compat.hpp"
30 #include "font/standard_colors.hpp"
31 #include "sdl/rect.hpp"
32 #include "sdl/input.hpp" // get_mouse_state
33 #include "sdl/utils.hpp" // blur_surface
34 #include "video.hpp"
35 
36 static lg::log_domain log_display("display");
37 #define ERR_DP LOG_STREAM(err, log_display)
38 #define WRN_DP LOG_STREAM(warn, log_display)
39 #define DBG_DP LOG_STREAM(debug, log_display)
40 #define ERR_G LOG_STREAM(err, lg::general)
41 
42 namespace {
43 bool is_in_dialog = false;
44 }
45 
46 namespace gui {
47 
48 //static initialization
49 const int ButtonHPadding = 10;
50 const int ButtonVPadding = 10;
51 
52 //note: style names are directly related to the panel image file names
54 
55 const int dialog_frame::title_border_w = 10;
56 const int dialog_frame::title_border_h = 5;
57 
58 
59 
60 bool in_dialog()
61 {
62  return is_in_dialog || gui2::is_in_dialog();
63 }
64 
66 {
67  is_in_dialog = true;
68 }
69 
71 {
73  int mousex, mousey;
74  sdl::get_mouse_state(&mousex, &mousey);
75  SDL_Event pb_event;
76  pb_event.type = SDL_MOUSEMOTION;
77  pb_event.motion.state = 0;
78  pb_event.motion.x = mousex;
79  pb_event.motion.y = mousey;
80  pb_event.motion.xrel = 0;
81  pb_event.motion.yrel = 0;
82  SDL_PushEvent(&pb_event);
83 }
84 
85 dialog_frame::dialog_frame(const std::string& title,
86  const style& style,
87  std::vector<button*>* buttons, button* help_button) :
88  title_(title),
89  dialog_style_(style),
90  buttons_(buttons),
91  help_button_(help_button),
92  dim_(),
93  top_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-top.png")),
94  bot_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-bottom.png")),
95  left_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-left.png")),
96  right_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-right.png")),
97  top_left_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-topleft.png")),
98  bot_left_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-botleft.png")),
99  top_right_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-topright.png")),
100  bot_right_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-botright.png")),
101  bg_(image::get_texture("dialogs/" + dialog_style_.panel + "-background.png")),
102  have_border_(top_ && bot_ && left_ && right_),
103  dirty_(true)
104 {
105  // Raise buttons so they are drawn on top.
106  // and BTW buttons_ being a pointer to a vector is fucking insane
107  for (button* b : *buttons_) {
109  }
110  if (help_button) {
112  }
113 }
114 
116 {
118 }
119 
121  interior(sdl::empty_rect), exterior(sdl::empty_rect), title(sdl::empty_rect), button_row(sdl::empty_rect)
122 {}
123 
125 {
126  return layout(rect.x, rect.y, rect.w, rect.h);
127 }
128 
130 {
131  int padding = 0;
132  if(have_border_) {
133  padding += top_.h();
134  }
135  if(!title_.empty()) {
137  }
138  return padding;
139 }
140 
141 void dialog_frame::set_dirty(bool dirty)
142 {
143  dirty_ = dirty;
144 }
145 
147  int padding = 0;
148  if(buttons_ != nullptr) {
149  for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
150  padding = std::max<int>((**b).height() + ButtonVPadding, padding);
151  }
152  }
153  if(have_border_) {
154  padding += bot_.h();
155  }
156  return padding;
157 }
158 
161  if(!title_.empty()) {
162  dim_.title = draw_title(false);
164  }
165  if(buttons_ != nullptr) {
166  for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
167  dim_.button_row.w += (**b).width() + ButtonHPadding;
168  dim_.button_row.h = std::max<int>((**b).height() + ButtonVPadding,dim_.button_row.h);
169  }
170 
172  dim_.button_row.y = y + h;
173 
175  }
176 
177  std::size_t buttons_width = dim_.button_row.w;
178 
179  if(help_button_ != nullptr) {
180  buttons_width += help_button_->width() + ButtonHPadding*2;
181  dim_.button_row.y = y + h;
182  }
183 
184  y -= dim_.title.h;
185  w = std::max(w, std::max(dim_.title.w, static_cast<int>(buttons_width)));
186  h += dim_.title.h + dim_.button_row.h;
187  dim_.button_row.x += x + w;
188 
189  rect bounds = video::game_canvas();
190  if(have_border_) {
191  bounds.x += left_.w();
192  bounds.y += top_.h();
193  bounds.w -= left_.w();
194  bounds.h -= top_.h();
195  }
196  if(x < bounds.x) {
197  w += x;
198  x = bounds.x;
199  }
200  if(y < bounds.y) {
201  h += y;
202  y = bounds.y;
203  }
204  if(x > bounds.w) {
205  w = 0;
206  } else if(x + w > bounds.w) {
207  w = bounds.w - x;
208  }
209  if(y > bounds.h) {
210  h = 0;
211  } else if(y + h > bounds.h) {
212  h = bounds.h - y;
213  }
214  dim_.interior.x = x;
215  dim_.interior.y = y;
216  dim_.interior.w = w;
217  dim_.interior.h = h;
218  if(have_border_) {
219  dim_.exterior.x = dim_.interior.x - left_.w();
220  dim_.exterior.y = dim_.interior.y - top_.h();
221  dim_.exterior.w = dim_.interior.w + left_.w() + right_.w();
222  dim_.exterior.h = dim_.interior.h + top_.h() + bot_.h();
223  } else {
225  }
228 
230 
231  return dim_;
232 }
233 
235 {
236  if(have_border_ == false) {
237  return;
238  }
239 
240  // Too much typing is bad for you.
241  const SDL_Rect& i = dim_.interior;
242  const SDL_Rect& e = dim_.exterior;
243 
244  if(top_) {
245  draw::blit(top_, {i.x, e.y, i.w, top_.h()});
246  }
247 
248  if(bot_) {
249  draw::blit(bot_, {i.x, i.y + i.h, i.w, bot_.h()});
250  }
251 
252  if(left_) {
253  draw::blit(left_, {e.x, i.y, left_.w(), i.h});
254  }
255 
256  if(right_) {
257  draw::blit(right_, {i.x + i.w, i.y, right_.w(), i.h});
258  }
259 
260  if(!top_left_ || !bot_left_ || !top_right_ || !bot_right_) {
261  return;
262  }
263 
265  {i.x - left_.w(), i.y - top_.h(), top_left_.w(), top_left_.h()});
266 
268  i.x - left_.w(),
269  i.y + i.h + bot_.h() - bot_left_.h(),
270  bot_left_.w(),
271  bot_left_.h()
272  });
273 
275  i.x + i.w + right_.w() - top_right_.w(),
276  i.y - top_.h(),
277  top_right_.w(),
278  top_right_.h(),
279  });
280 
282  i.x + i.w + right_.w() - bot_right_.w(),
283  i.y + i.h + bot_.h() - bot_right_.h(),
284  bot_right_.w(),
285  bot_right_.h()
286  });
287 }
288 
290 {
292  // This is no longer used by anything.
293  // The only thing that uses dialog_frame is help/help.cpp,
294  // and it uses the default style with no blur.
295  ERR_DP << "GUI1 dialog_frame blur has been removed";
296  }
297 
298  if (!bg_) {
299  ERR_DP << "could not find dialog background '" << dialog_style_.panel << "'";
300  return;
301  }
302 
303  auto clipper = draw::reduce_clip(dim_.interior);
304  for(int i = 0; i < dim_.interior.w; i += bg_.w()) {
305  for(int j = 0; j < dim_.interior.h; j += bg_.h()) {
306  SDL_Rect src {0,0,0,0};
307  src.w = std::min(dim_.interior.w - i, bg_.w());
308  src.h = std::min(dim_.interior.h - j, bg_.h());
309  SDL_Rect dst = src;
310  dst.x = dim_.interior.x + i;
311  dst.y = dim_.interior.y + j;
312  draw::blit(bg_, dst);
313  }
314  }
315 }
316 
317 rect dialog_frame::draw_title(bool actually_draw)
318 {
319  rect r = video::game_canvas();
320  return font::pango_draw_text(
321  actually_draw, r, font::SIZE_TITLE, font::TITLE_COLOR, title_,
323  );
324 }
325 
327 {
328  //draw background
329  draw_background();
330 
331  //draw frame border
332  draw_border();
333 
334  //draw title
335  if (!title_.empty()) {
336  draw_title(true);
337  }
338 }
339 
341 {
342  if (!dirty_) {
343  return;
344  }
345 
346  // Layout buttons
347  SDL_Rect buttons_area = dim_.button_row;
348  if(buttons_ != nullptr) {
349 #ifdef OK_BUTTON_ON_RIGHT
350  std::reverse(buttons_->begin(),buttons_->end());
351 #endif
352  for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
353  (**b).set_location(buttons_area.x, buttons_area.y);
354  buttons_area.x += (**b).width() + ButtonHPadding;
355  }
356  }
357 
358  // Layout help button, if any
359  if(help_button_ != nullptr) {
361  }
362 
363  dirty_ = false;
364 }
365 
366 bool dialog_frame::expose(const rect& region)
367 {
368  DBG_DP << "dialog_frame::expose " << region;
369  // Just draw everthing.
370  draw();
371  return true;
372 }
373 
375 {
376  return dim_.exterior;
377 }
378 
379 }
A button is a control that can be pushed to start an action or close a dialog.
Definition: button.hpp:52
dimension_measurements dim_
std::vector< button * > * buttons_
int top_padding() const
dialog_frame(const std::string &title="", const style &dialog_style=default_style, std::vector< button * > *buttons=nullptr, button *help_button=nullptr)
Definition: show_dialog.cpp:85
static const style default_style
Definition: show_dialog.hpp:67
void set_dirty(bool dirty=true)
static const int title_border_h
Definition: show_dialog.hpp:66
virtual void layout() override
Called by draw_manager to validate layout.
virtual bool expose(const rect &region) override
Called by draw_manager when it believes a redraw is necessary.
virtual rect screen_location() override
The current draw location of the window, on the screen.
std::string title_
const style & dialog_style_
static const int title_border_w
Definition: show_dialog.hpp:66
int bottom_padding() const
rect draw_title(bool actually_draw)
virtual void set_location(const SDL_Rect &rect)
Definition: widget.cpp:69
int width() const
Definition: widget.cpp:113
int w() const
The draw-space width of the texture, in pixels.
Definition: texture.hpp:105
int h() const
The draw-space height of the texture, in pixels.
Definition: texture.hpp:114
Drawing functions, for drawing things on the screen.
std::size_t i
Definition: function.cpp:968
int w
Contains functions for cleanly handling SDL input.
Standard logging facilities (interface).
@ NORMAL
Definition: cursor.hpp:29
void invalidate_region(const rect &region)
Mark a region of the screen as requiring redraw.
void raise_drawable(top_level_drawable *tld)
Raise a TLD to the top of the drawing stack.
clip_setter reduce_clip(const SDL_Rect &clip)
Set the clipping area to the intersection of the current clipping area and the given rectangle.
Definition: draw.cpp:448
void blit(const texture &tex, const SDL_Rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition: draw.cpp:301
int get_max_height(unsigned size, font::family_class fclass, pango_text::FONT_STYLE style)
Returns the maximum glyph height of a font, in pixels.
Definition: text.cpp:967
const color_t TITLE_COLOR
rect pango_draw_text(bool actually_draw, const rect &area, int size, const color_t &color, const std::string &text, int x, int y, bool use_tooltips, pango_text::FONT_STYLE style)
Draws text on the screen.
const int SIZE_TITLE
Definition: constants.cpp:31
bool is_in_dialog()
Is a dialog open?
Definition: handler.cpp:1088
General purpose widgets.
const int ButtonHPadding
Definition: show_dialog.cpp:49
const int ButtonVPadding
Definition: show_dialog.cpp:50
bool in_dialog()
Definition: show_dialog.cpp:60
Functions to load and save images from/to disk.
texture get_texture(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image texture suitable for hardware-accelerated rendering.
Definition: picture.cpp:985
constexpr const SDL_Rect empty_rect
Definition: rect.hpp:30
uint32_t get_mouse_state(int *x, int *y)
A wrapper for SDL_GetMouseState that gives coordinates in draw space.
Definition: input.cpp:27
rect game_canvas()
The game canvas area, in drawing coordinates.
Definition: video.cpp:421
Contains the SDL_Rect helper code.
Transitional API for porting SDL_ttf-based code to Pango.
static lg::log_domain log_display("display")
#define ERR_DP
Definition: show_dialog.cpp:37
#define DBG_DP
Definition: show_dialog.cpp:39
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:47
#define e
#define h
#define b