The Battle for Wesnoth  1.19.25+dev
floating_textbox.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2025
3  by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 #include "floating_textbox.hpp"
18 
19 #include "display_chat_manager.hpp"
20 #include "floating_label.hpp"
21 #include "font/standard_colors.hpp"
22 #include "game_display.hpp"
24 #include "video.hpp"
25 #include "log.hpp"
26 
27 #include <SDL3/SDL_keyboard.h>
28 #include <ctime>
29 
30 static lg::log_domain log_display("display");
31 #define ERR_DP LOG_STREAM(err, log_display)
32 
33 namespace gui
34 {
36  box_(nullptr),
37  check_(nullptr),
38  mode_(TEXTBOX_NONE),
39  label_string_(),
40  label_(0),
41  command_history_()
42 {}
43 
45 {
46  if(!active()) {
47  return;
48  }
49 
50  if(check_ != nullptr && mode_ == TEXTBOX_MESSAGE) {
52  }
53  box_.reset(nullptr);
54  check_.reset(nullptr);
57 
58 #if defined(__ANDROID__) || defined(__IPHONEOS__)
59  // Hide onscreen keyboard
60  SDL_StopTextInput(video::get_window());
61 #endif
62 }
63 
65 {
66  if(box_ == nullptr) {
67  return;
68  }
69 
70  const rect& area = gui.map_outside_area();
71 
72  const int border_size = 10;
73 
74 #ifdef __ANDROID__
75  // On Android the Onscreen keyboard hides this box if aligned to bottom,
76  // so we align it to top instead.
77  const int ypos = area.y + border_size - (check_ != nullptr ? check_->height() + border_size : 0);
78 #else
79  const int ypos = area.y + area.h - 30 - (check_ != nullptr ? check_->height() + border_size : 0);
80 #endif
81 
82  if(label_ != 0) {
84  }
85 
88  flabel.set_position(area.x + border_size, ypos);
90  flabel.set_clip_rect(area);
91 
92  // This label should always be a single line and never wrap. Without explicitly setting -1, Pango
93  // will measure it against a full-screen-wide wrap box (floating label's clip_rect width), and for
94  // a pure RTL caption (eg Arabic, Hebrew) Pango inflates the measured width to practically the
95  // entire screen width - therefore there is no room for the text-box and it appears as though it
96  // doesn't work for RTL text. Refer GitHub #8673.
97  flabel.set_width(-1);
98 
100 
101  if(label_ == 0) {
102  return;
103  }
104 
105  const rect& label_area = font::get_floating_label_rect(label_);
106  const int textbox_width = area.w - label_area.w - border_size * 3;
107 
108  if(textbox_width <= 0) {
110  return;
111  }
112 
113  if(box_ != nullptr) {
114  const rect rect {
115  area.x + label_area.w + border_size * 2
116  , ypos
117  , textbox_width
118  , box_->height()
119  };
120 
121  box_->set_location(rect);
122  }
123 
124  if(check_ != nullptr) {
125  check_->set_location(box_->location().x,box_->location().y + box_->location().h + border_size);
126  }
127 }
128 
130  gui::TEXTBOX_MODE mode, const std::string& label,
131  const std::string& check_label, bool checked, game_display& gui)
132 {
133  close();
134 
136  mode_ = mode;
137 
138  if(!check_label.empty()) {
139  check_.reset(new gui::button(check_label,gui::button::TYPE_CHECK));
140  check_->set_check(checked);
141  }
142 
143  box_.reset(new gui::textbox(100,"",true,256,font::SIZE_NORMAL,0.8,0.6));
144 
146 
147 #if defined(__ANDROID__) || defined(__IPHONEOS__)
148  // Show onscreen keyboard
149  SDL_StartTextInput(video::get_window());
150 #endif
151 }
152 
153 void floating_textbox::tab(const std::set<std::string>& dictionary)
154 {
155  if(active() == false) {
156  return;
157  }
158 
159  std::string text = box_->text();
160  std::vector<std::string> matches(dictionary.begin(), dictionary.end());
161  const bool line_start = utils::word_completion(text, matches);
162 
163  if(matches.empty()) {
164  return;
165  }
166 
167  if(matches.size() == 1 && mode_ == gui::TEXTBOX_MESSAGE) {
168  text.append(line_start ? ": " : " ");
169  } else if(matches.size() > 1) {
170  std::string completion_list = utils::join(matches, " ");
172  std::chrono::system_clock::now(), "", 0, completion_list, events::chat_handler::MESSAGE_PRIVATE, false);
173  }
174  box_->set_text(text);
175 }
176 
177 void floating_textbox::memorize_command(const std::string& command)
178 {
179  if(command.empty()) {
180  return;
181  }
182 
183  auto prev = std::find(command_history_.begin(), command_history_.end(), command);
184 
185  if(prev != command_history_.end()) {
186  command_history_.erase(prev);
187  }
188  command_history_.emplace_back(command);
189 }
190 }
map_location prev
Definition: astarsearch.cpp:64
void add_chat_message(const std::chrono::system_clock::time_point &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
void set_position(double xpos, double ypos)
void set_alignment(ALIGN align)
void set_color(const color_t &color)
void set_clip_rect(const rect &r)
static game_display * get_singleton()
display_chat_manager & get_chat_manager()
@ TYPE_CHECK
Definition: button.hpp:29
void show(gui::TEXTBOX_MODE mode, const std::string &label, const std::string &check_label, bool checked, game_display &gui)
void memorize_command(const std::string &command)
TEXTBOX_MODE mode() const
void update_location(game_display &gui)
void tab(const std::set< std::string > &dictionary)
std::unique_ptr< gui::button > check_
std::vector< std::string > command_history_
std::unique_ptr< gui::textbox > box_
static prefs & get()
void set_message_private(bool value)
static lg::log_domain log_display("display")
std::string label
What to show in the filter's drop-down list.
Definition: manager.cpp:201
Standard logging facilities (interface).
const color_t YELLOW_COLOR
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
rect get_floating_label_rect(int handle)
void remove_floating_label(int handle, const std::chrono::milliseconds &fadeout)
removes the floating label given by 'handle' from the screen
const int SIZE_NORMAL
Definition: constants.cpp:20
General purpose widgets.
@ TEXTBOX_MESSAGE
std::string join(const Range &v, const std::string &s=",")
Generates a new string joining container items in a list.
bool word_completion(std::string &text, std::vector< std::string > &wordlist)
Try to complete the last word of 'text' with the 'wordlist'.
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:141
SDL_Window * get_window()
Definition: video.cpp:690
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:49