1 /*
2 Copyright (C) 2009 - 2018 by Mark de Wever <koraq@xs4all.nl>
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13 */
14
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16
17 #include "gui/widgets/repeating_button.hpp"
18
19 #include "gui/core/log.hpp"
20 #include "gui/core/timer.hpp"
21 #include "gui/core/register_widget.hpp"
22 #include "gui/widgets/settings.hpp"
23 #include "gui/widgets/window.hpp"
24 #include "sound.hpp"
25
26 #include "utils/functional.hpp"
27
28 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
29 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
30
31 namespace gui2
32 {
33
34 // ------------ WIDGET -----------{
35
REGISTER_WIDGET(repeating_button)36 REGISTER_WIDGET(repeating_button)
37
38 repeating_button::repeating_button(const implementation::builder_repeating_button& builder)
39 : styled_widget(builder, type())
40 , clickable_item()
41 , state_(ENABLED)
42 , repeat_timer_(0)
43 {
44 connect_signal<event::MOUSE_ENTER>(std::bind(
45 &repeating_button::signal_handler_mouse_enter, this, _2, _3));
46 connect_signal<event::MOUSE_LEAVE>(std::bind(
47 &repeating_button::signal_handler_mouse_leave, this, _2, _3));
48
49 connect_signal<event::LEFT_BUTTON_DOWN>(std::bind(
50 &repeating_button::signal_handler_left_button_down, this, _2, _3));
51 connect_signal<event::LEFT_BUTTON_UP>(std::bind(
52 &repeating_button::signal_handler_left_button_up, this, _2, _3));
53 }
54
~repeating_button()55 repeating_button::~repeating_button()
56 {
57 if(repeat_timer_) {
58 remove_timer(repeat_timer_);
59 }
60 }
61
connect_signal_mouse_left_down(const event::signal_function & signal)62 void repeating_button::connect_signal_mouse_left_down(
63 const event::signal_function& signal)
64 {
65 connect_signal<event::LEFT_BUTTON_DOWN>(signal);
66 }
67
disconnect_signal_mouse_left_down(const event::signal_function & signal)68 void repeating_button::disconnect_signal_mouse_left_down(
69 const event::signal_function& signal)
70 {
71 disconnect_signal<event::LEFT_BUTTON_DOWN>(signal);
72 }
73
set_active(const bool active)74 void repeating_button::set_active(const bool active)
75 {
76 if(get_active() != active) {
77 set_state(active ? ENABLED : DISABLED);
78 }
79 }
80
get_active() const81 bool repeating_button::get_active() const
82 {
83 return state_ != DISABLED;
84 }
85
get_state() const86 unsigned repeating_button::get_state() const
87 {
88 return state_;
89 }
90
set_state(const state_t state)91 void repeating_button::set_state(const state_t state)
92 {
93 if(state != state_) {
94 state_ = state;
95 set_is_dirty(true);
96
97 if(state_ == DISABLED && repeat_timer_) {
98 remove_timer(repeat_timer_);
99 repeat_timer_ = 0;
100 }
101 }
102 }
103
signal_handler_mouse_enter(const event::ui_event event,bool & handled)104 void repeating_button::signal_handler_mouse_enter(const event::ui_event event,
105 bool& handled)
106 {
107 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
108
109 set_state(FOCUSED);
110 handled = true;
111 }
112
signal_handler_mouse_leave(const event::ui_event event,bool & handled)113 void repeating_button::signal_handler_mouse_leave(const event::ui_event event,
114 bool& handled)
115 {
116 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
117
118 set_state(ENABLED);
119 handled = true;
120 }
121
122 void
signal_handler_left_button_down(const event::ui_event event,bool & handled)123 repeating_button::signal_handler_left_button_down(const event::ui_event event,
124 bool& handled)
125 {
126 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
127
128 // If the timer isn't set it's the initial down event.
129 if(!repeat_timer_) {
130
131 // mimic the old gui and only play the sound once.
132 sound::play_UI_sound(settings::sound_button_click);
133
134 window* window = get_window();
135 if(window) {
136 repeat_timer_ = add_timer(settings::repeat_button_repeat_time,
137 [this, window](unsigned int) {
138 window->fire(event::LEFT_BUTTON_DOWN, *this);
139 },true);
140
141 window->mouse_capture();
142 }
143
144 set_state(PRESSED);
145 }
146
147 handled = true;
148 }
149
signal_handler_left_button_up(const event::ui_event event,bool & handled)150 void repeating_button::signal_handler_left_button_up(const event::ui_event event,
151 bool& handled)
152 {
153 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
154
155 if(repeat_timer_) {
156 remove_timer(repeat_timer_);
157 repeat_timer_ = 0;
158 }
159
160 if(get_active()) {
161 set_state(FOCUSED);
162 }
163 handled = true;
164 }
165
166 // }---------- DEFINITION ---------{
167
repeating_button_definition(const config & cfg)168 repeating_button_definition::repeating_button_definition(const config& cfg)
169 : styled_widget_definition(cfg)
170 {
171 DBG_GUI_P << "Parsing repeating button " << id << '\n';
172
173 load_resolutions<resolution>(cfg);
174 }
175
176 /*WIKI
177 * @page = GUIWidgetDefinitionWML
178 * @order = 1_repeating_button
179 *
180 * == Repeating button ==
181 *
182 * @macro = repeating_button_description
183 *
184 * The following states exist:
185 * * state_enabled, the repeating_button is enabled.
186 * * state_disabled, the repeating_button is disabled.
187 * * state_pressed, the left mouse repeating_button is down.
188 * * state_focused, the mouse is over the repeating_button.
189 * @begin{parent}{name="gui/"}
190 * @begin{tag}{name="repeating_button_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
191 * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="generic/widget_definition/resolution"}
192 * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
193 * @end{tag}{name="state_enabled"}
194 * @begin{tag}{name="state_disabled"}{min=0}{max=1}{super="generic/state"}
195 * @end{tag}{name="state_disabled"}
196 * @begin{tag}{name="state_pressed"}{min=0}{max=1}{super="generic/state"}
197 * @end{tag}{name="state_pressed"}
198 * @begin{tag}{name="state_focused"}{min=0}{max=1}{super="generic/state"}
199 * @end{tag}{name="state_focused"}
200 * @end{tag}{name="resolution"}
201 * @end{tag}{name="repeating_button_definition"}
202 * @end{parent}{name="gui/"}
203 */
resolution(const config & cfg)204 repeating_button_definition::resolution::resolution(const config& cfg)
205 : resolution_definition(cfg)
206 {
207 // Note the order should be the same as the enum state_t in
208 // repeating_button.hpp.
209 state.emplace_back(cfg.child("state_enabled"));
210 state.emplace_back(cfg.child("state_disabled"));
211 state.emplace_back(cfg.child("state_pressed"));
212 state.emplace_back(cfg.child("state_focused"));
213 }
214
215 // }---------- BUILDER -----------{
216
217 /*WIKI_MACRO
218 * @begin{macro}{repeating_button_description}
219 *
220 * A repeating_button is a styled_widget that can be pushed down and repeat a
221 * certain action. Once the button is down every x milliseconds it is
222 * down a new down event is triggered.
223 * @end{macro}
224 */
225
226 /*WIKI
227 * @page = GUIWidgetInstanceWML
228 * @order = 2_repeating_button
229 *
230 * == Repeating button ==
231 *
232 * @macro = repeating_button_description
233 * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
234 * @begin{tag}{name="repeating_button"}{min=0}{max=-1}{super="gui/window/resolution/grid/row/column/button"}
235 * @end{tag}{name="repeating_button"}
236 * @end{parent}{name="gui/window/resolution/grid/row/column/"}
237 */
238
239 namespace implementation
240 {
241
builder_repeating_button(const config & cfg)242 builder_repeating_button::builder_repeating_button(const config& cfg)
243 : builder_styled_widget(cfg)
244 {
245 }
246
build() const247 widget* builder_repeating_button::build() const
248 {
249 repeating_button* widget = new repeating_button(*this);
250
251 DBG_GUI_G << "Window builder: placed repeating button '" << id
252 << "' with definition '" << definition << "'.\n";
253
254 return widget;
255 }
256
257 } // namespace implementation
258
259 // }------------ END --------------
260
261 } // namespace gui2
262