The Battle for Wesnoth  1.19.0+dev
show_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
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 "picture.hpp"
23 #include "gettext.hpp"
24 #include "gui/core/event/handler.hpp" // is_in_dialog
25 #include "log.hpp"
26 #include "font/sdl_ttf_compat.hpp"
27 #include "font/standard_colors.hpp"
28 #include "sdl/rect.hpp"
29 #include "sdl/input.hpp" // get_mouse_state
30 #include "video.hpp"
31 
32 static lg::log_domain log_display("display");
33 #define ERR_DP LOG_STREAM(err, log_display)
34 #define WRN_DP LOG_STREAM(warn, log_display)
35 #define DBG_DP LOG_STREAM(debug, log_display)
36 #define ERR_G LOG_STREAM(err, lg::general)
37 
38 namespace {
39 bool is_in_dialog = false;
40 }
41 
42 namespace gui {
43 
44 //static initialization
45 const int ButtonHPadding = 10;
46 const int ButtonVPadding = 10;
47 
48 //note: style names are directly related to the panel image file names
50 
51 const int dialog_frame::title_border_w = 10;
52 const int dialog_frame::title_border_h = 5;
53 
54 
55 
56 bool in_dialog()
57 {
58  return is_in_dialog || gui2::is_in_dialog();
59 }
60 
62 {
63  is_in_dialog = true;
64 }
65 
67 {
69  int mousex, mousey;
70  sdl::get_mouse_state(&mousex, &mousey);
71  SDL_Event pb_event;
72  pb_event.type = SDL_MOUSEMOTION;
73  pb_event.motion.state = 0;
74  pb_event.motion.x = mousex;
75  pb_event.motion.y = mousey;
76  pb_event.motion.xrel = 0;
77  pb_event.motion.yrel = 0;
78  SDL_PushEvent(&pb_event);
79 }
80 
81 dialog_frame::dialog_frame(const std::string& title,
82  const style& style,
83  std::vector<button*>* buttons, button* help_button) :
84  title_(title),
85  dialog_style_(style),
86  buttons_(buttons),
87  help_button_(help_button),
88  dim_(),
89  top_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-top.png")),
90  bot_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-bottom.png")),
91  left_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-left.png")),
92  right_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-right.png")),
93  top_left_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-topleft.png")),
94  bot_left_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-botleft.png")),
95  top_right_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-topright.png")),
96  bot_right_(image::get_texture("dialogs/" + dialog_style_.panel + "-border-botright.png")),
97  bg_(image::get_texture("dialogs/" + dialog_style_.panel + "-background.png")),
98  have_border_(top_ && bot_ && left_ && right_),
99  dirty_(true)
100 {
101  // Raise buttons so they are drawn on top.
102  // and BTW buttons_ being a pointer to a vector is fucking insane
103  for (button* b : *buttons_) {
105  }
106  if (help_button) {
108  }
109 }
110 
112 {
114 }
115 
117  interior(sdl::empty_rect), exterior(sdl::empty_rect), title(sdl::empty_rect), button_row(sdl::empty_rect)
118 {}
119 
121 {
122  return layout(rect.x, rect.y, rect.w, rect.h);
123 }
124 
126 {
127  int padding = 0;
128  if(have_border_) {
129  padding += top_.h();
130  }
131  if(!title_.empty()) {
133  }
134  return padding;
135 }
136 
137 void dialog_frame::set_dirty(bool dirty)
138 {
139  dirty_ = dirty;
140 }
141 
143  int padding = 0;
144  if(buttons_ != nullptr) {
145  for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
146  padding = std::max<int>((**b).height() + ButtonVPadding, padding);
147  }
148  }
149  if(have_border_) {
150  padding += bot_.h();
151  }
152  return padding;
153 }
154 
157  if(!title_.empty()) {
158  dim_.title = draw_title(false);
160  }
161  if(buttons_ != nullptr) {
162  for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
163  dim_.button_row.w += (**b).width() + ButtonHPadding;
164  dim_.button_row.h = std::max<int>((**b).height() + ButtonVPadding,dim_.button_row.h);
165  }
166 
168  dim_.button_row.y = y + h;
169 
171  }
172 
173  std::size_t buttons_width = dim_.button_row.w;
174 
175  if(help_button_ != nullptr) {
176  buttons_width += help_button_->width() + ButtonHPadding*2;
177  dim_.button_row.y = y + h;
178  }
179 
180  y -= dim_.title.h;
181  w = std::max(w, std::max(dim_.title.w, static_cast<int>(buttons_width)));
182  h += dim_.title.h + dim_.button_row.h;
183  dim_.button_row.x += x + w;
184 
185  rect bounds = video::game_canvas();
186  if(have_border_) {
187  bounds.x += left_.w();
188  bounds.y += top_.h();
189  bounds.w -= left_.w();
190  bounds.h -= top_.h();
191  }
192  if(x < bounds.x) {
193  w += x;
194  x = bounds.x;
195  }
196  if(y < bounds.y) {
197  h += y;
198  y = bounds.y;
199  }
200  if(x > bounds.w) {
201  w = 0;
202  } else if(x + w > bounds.w) {
203  w = bounds.w - x;
204  }
205  if(y > bounds.h) {
206  h = 0;
207  } else if(y + h > bounds.h) {
208  h = bounds.h - y;
209  }
210  dim_.interior.x = x;
211  dim_.interior.y = y;
212  dim_.interior.w = w;
213  dim_.interior.h = h;
214  if(have_border_) {
215  dim_.exterior.x = dim_.interior.x - left_.w();
216  dim_.exterior.y = dim_.interior.y - top_.h();
217  dim_.exterior.w = dim_.interior.w + left_.w() + right_.w();
218  dim_.exterior.h = dim_.interior.h + top_.h() + bot_.h();
219  } else {
221  }
224 
226 
227  return dim_;
228 }
229 
231 {
232  if(have_border_ == false) {
233  return;
234  }
235 
236  // Too much typing is bad for you.
237  const SDL_Rect& i = dim_.interior;
238  const SDL_Rect& e = dim_.exterior;
239 
240  if(top_) {
241  draw::blit(top_, {i.x, e.y, i.w, top_.h()});
242  }
243 
244  if(bot_) {
245  draw::blit(bot_, {i.x, i.y + i.h, i.w, bot_.h()});
246  }
247 
248  if(left_) {
249  draw::blit(left_, {e.x, i.y, left_.w(), i.h});
250  }
251 
252  if(right_) {
253  draw::blit(right_, {i.x + i.w, i.y, right_.w(), i.h});
254  }
255 
256  if(!top_left_ || !bot_left_ || !top_right_ || !bot_right_) {
257  return;
258  }
259 
261  {i.x - left_.w(), i.y - top_.h(), top_left_.w(), top_left_.h()});
262 
264  i.x - left_.w(),
265  i.y + i.h + bot_.h() - bot_left_.h(),
266  bot_left_.w(),
267  bot_left_.h()
268  });
269 
271  i.x + i.w + right_.w() - top_right_.w(),
272  i.y - top_.h(),
273  top_right_.w(),
274  top_right_.h(),
275  });
276 
278  i.x + i.w + right_.w() - bot_right_.w(),
279  i.y + i.h + bot_.h() - bot_right_.h(),
280  bot_right_.w(),
281  bot_right_.h()
282  });
283 }
284 
286 {
288  // This is no longer used by anything.
289  // The only thing that uses dialog_frame is help/help.cpp,
290  // and it uses the default style with no blur.
291  ERR_DP << "GUI1 dialog_frame blur has been removed";
292  }
293 
294  if (!bg_) {
295  ERR_DP << "could not find dialog background '" << dialog_style_.panel << "'";
296  return;
297  }
298 
299  auto clipper = draw::reduce_clip(dim_.interior);
300  for(int i = 0; i < dim_.interior.w; i += bg_.w()) {
301  for(int j = 0; j < dim_.interior.h; j += bg_.h()) {
302  SDL_Rect src {0,0,0,0};
303  src.w = std::min(dim_.interior.w - i, bg_.w());
304  src.h = std::min(dim_.interior.h - j, bg_.h());
305  SDL_Rect dst = src;
306  dst.x = dim_.interior.x + i;
307  dst.y = dim_.interior.y + j;
308  draw::blit(bg_, dst);
309  }
310  }
311 }
312 
313 rect dialog_frame::draw_title(bool actually_draw)
314 {
315  rect r = video::game_canvas();
316  return font::pango_draw_text(
317  actually_draw, r, font::SIZE_TITLE, font::TITLE_COLOR, title_,
319  );
320 }
321 
323 {
324  //draw background
325  draw_background();
326 
327  //draw frame border
328  draw_border();
329 
330  //draw title
331  if (!title_.empty()) {
332  draw_title(true);
333  }
334 }
335 
337 {
338  if (!dirty_) {
339  return;
340  }
341 
342  // Layout buttons
343  SDL_Rect buttons_area = dim_.button_row;
344  if(buttons_ != nullptr) {
345 #ifdef OK_BUTTON_ON_RIGHT
346  std::reverse(buttons_->begin(),buttons_->end());
347 #endif
348  for(std::vector<button*>::const_iterator b = buttons_->begin(); b != buttons_->end(); ++b) {
349  (**b).set_location(buttons_area.x, buttons_area.y);
350  buttons_area.x += (**b).width() + ButtonHPadding;
351  }
352  }
353 
354  // Layout help button, if any
355  if(help_button_ != nullptr) {
357  }
358 
359  dirty_ = false;
360 }
361 
362 bool dialog_frame::expose(const rect& region)
363 {
364  DBG_DP << "dialog_frame::expose " << region;
365  // Just draw everthing.
366  draw();
367  return true;
368 }
369 
371 {
372  return dim_.exterior;
373 }
374 
375 }
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:81
static const style default_style
Definition: show_dialog.hpp:65
void set_dirty(bool dirty=true)
static const int title_border_h
Definition: show_dialog.hpp:64
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:64
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:28
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:502
void blit(const texture &tex, const SDL_Rect &dst)
Draws a texture, or part of a texture, at the given location.
Definition: draw.cpp:310
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:1180
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:1085
General purpose widgets.
const int ButtonHPadding
Definition: show_dialog.cpp:45
const int ButtonVPadding
Definition: show_dialog.cpp:46
bool in_dialog()
Definition: show_dialog.cpp:56
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:960
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:429
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:33
#define DBG_DP
Definition: show_dialog.cpp:35
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:47
#define e
#define h
#define b