1 // -*- C++ -*-
2 /* GG is a GUI for OpenGL.
3    Copyright (C) 2003-2008 T. Zachary Laine
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public License
7    as published by the Free Software Foundation; either version 2.1
8    of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA
19 
20    If you do not wish to comply with the terms of the LGPL please
21    contact the author as other terms are available for a fee.
22 
23    Zach Laine
24    whatwasthataddress@gmail.com */
25 
26 /** \file Button.h \brief Contains the Button push-button control class; the
27     StateButton control class, which represents check boxes and radio buttons;
28     and the RadioButtonGroup control class, which allows multiple radio
29     buttons to be combined into a single control. */
30 
31 #ifndef _GG_Button_h_
32 #define _GG_Button_h_
33 
34 #include <GG/ClrConstants.h>
35 #include <GG/TextControl.h>
36 #include <GG/Enum.h>
37 
38 #include <boost/signals2/signal.hpp>
39 
40 
41 namespace GG {
42 
43 /** \brief This is a basic button control.
44 
45     Has three states: BN_UNPRESSED, BN_PRESSED, and BN_ROLLOVER.  BN_ROLLOVER
46     is when the cursor "rolls over" the button, without depressing it,
47     allowing rollover effects on the button.  To create a bitmap button,
48     simply set the unpressed, pressed, and/or rollover graphics to the desired
49     SubTextures. \see GG::SubTexture */
50 class GG_API Button : public Control
51 {
52 public:
53     /// the states of being for a GG::Button
54     GG_CLASS_ENUM(ButtonState,
55         BN_PRESSED,    ///< The button is being pressed by the user, and the cursor is over the button
56         BN_UNPRESSED,  ///< The button is unpressed
57         BN_ROLLOVER    ///< The button has the cursor over it, but is unpressed
58     )
59 
60     /** \name Signal Types */ ///@{
61     /** Emitted when the button is clicked by the user */
62     typedef boost::signals2::signal<void ()> ClickedSignalType;
63     //@}
64 
65    /** \name Structors */ ///@{
66     Button(const std::string& str, const std::shared_ptr<Font>& font, Clr color,
67            Clr text_color = CLR_BLACK, Flags<WndFlag> flags = INTERACTIVE);
68     //@}
69     void CompleteConstruction() override;
70 
71     /** \name Accessors */ ///@{
72     Pt MinUsableSize() const override;
73 
74     /** Returns button state \see ButtonState */
75     ButtonState State() const;
76 
77     const std::string& Text() const;             ///< Returns the label to be used as the button label
78     const SubTexture& UnpressedGraphic() const;  ///< Returns the SubTexture to be used as the image of the button when unpressed
79     const SubTexture& PressedGraphic() const;    ///< Returns the SubTexture to be used as the image of the button when pressed
80     const SubTexture& RolloverGraphic() const;   ///< Returns the SubTexture to be used as the image of the button when it contains the cursor, but is not pressed
81 
82     /** The left clicked signal object for this Button */
83     mutable ClickedSignalType LeftClickedSignal;
84     /** The right clicked signal object for this Button */
85     mutable ClickedSignalType RightClickedSignal;
86     /** The left pressed signal object for this Button */
87     mutable ClickedSignalType LeftPressedSignal;
88     /** The right pressed signal object for this Button */
89     mutable ClickedSignalType RightPressedSignal;
90     //@}
91 
92     /** \name Mutators */ ///@{
93     void Show() override;
94     void Render() override;
95     void SizeMove(const Pt& ul, const Pt& lr) override;
96 
97     /** Sets the control's color; does not affect the text color. */
98     void SetColor(Clr c) override;
99 
100     /** Sets button state programmatically \see ButtonState */
101     void SetState(ButtonState state);
102 
103     void SetText(const std::string& text);          ///< Sets the text to be used as the button label
104     void SetUnpressedGraphic(const SubTexture& st); ///< Sets the SubTexture to be used as the image of the button when unpressed
105     void SetPressedGraphic(const SubTexture& st);   ///< Sets the SubTexture to be used as the image of the button when pressed
106     void SetRolloverGraphic(const SubTexture& st);  ///< Sets the SubTexture to be used as the image of the button when it contains the cursor, but is not pressed
107     //@}
108 
109 protected:
110     /** \name Mutators */ ///@{
111     void LButtonDown(const Pt& pt, Flags<ModKey> mod_keys) override;
112     void LDrag(const Pt& pt, const Pt& move, Flags<ModKey> mod_keys) override;
113     void LButtonUp(const Pt& pt, Flags<ModKey> mod_keys) override;
114     void LClick(const Pt& pt, Flags<ModKey> mod_keys) override;
115     void RButtonDown(const Pt& pt, Flags<ModKey> mod_keys) override;
116     void RDrag(const Pt& pt, const Pt& move, Flags<ModKey> mod_keys) override;
117     void RButtonUp(const Pt& pt, Flags<ModKey> mod_keys) override;
118     void RClick(const Pt& pt, Flags<ModKey> mod_keys) override;
119     void MouseEnter(const Pt& pt, Flags<ModKey> mod_keys) override;
120     void MouseHere(const Pt& pt, Flags<ModKey> mod_keys) override;
121     void MouseLeave() override;
122 
123     /** Draws the button unpressed.  If an unpressed graphic has been supplied, it is used. */
124     virtual void RenderUnpressed();
125     /** Draws the button pressed.  If an pressed graphic has been supplied, it is used. */
126     virtual void RenderPressed();
127     /** Draws the button rolled-over.  If an rollover graphic has been supplied, it is used. */
128     virtual void RenderRollover();
129     //@}
130 
131     std::shared_ptr<TextControl> m_label;   ///< Label used to display text
132 
133 private:
134     void RenderDefault();     ///< This just draws the default unadorned square-and-rectangle button
135 
136     ButtonState    m_state;             ///< Button is always in exactly one of the ButtonState states above
137     SubTexture     m_unpressed_graphic; ///< Graphic used to display button when it's unpressed
138     SubTexture     m_pressed_graphic;   ///< Graphic used to display button when it's depressed
139     SubTexture     m_rollover_graphic;  ///< Graphic used to display button when it's under the mouse and not pressed
140 };
141 
142 
143 class StateButtonRepresenter;
144 
145 
146 /** \brief This is a basic state button control.
147 
148     This class is for checkboxes and radio buttons, etc.  The button/checkbox
149     area is determined from the text height and format; the button height and
150     width will be the text height, and the the button will be positioned to
151     the left of the text and vertically the same as the text, unless the text
152     is centered, in which case the button and text will be centered, and the
153     button will appear above or below the text.  Whenever there is not room to
154     place the button and the text in the proper orientation because the entire
155     control's size is too small, the button and text are positioned in their
156     default spots (button on left, text on right, centered vertically). */
157 class GG_API StateButton : public Control
158 {
159 public:
160     /// the states of being for a GG::Button
161     GG_CLASS_ENUM(ButtonState,
162         BN_PRESSED,    ///< The button is being pressed by the user, and the cursor is over the button
163         BN_UNPRESSED,  ///< The button is unpressed
164         BN_ROLLOVER    ///< The button has the cursor over it, but is unpressed
165     )
166 
167     /** \name Signal Types */ ///@{
168     /** Emitted when the StateButton is checked or unchecked; the checked or
169         unchecked status is indicated by the bool parameter */
170     typedef boost::signals2::signal<void (bool)> CheckedSignalType;
171     //@}
172 
173     /** \name Structors */ ///@{
174     StateButton(const std::string& str, const std::shared_ptr<Font>& font, Flags<TextFormat> format,
175                 Clr color, std::shared_ptr<StateButtonRepresenter> representer, Clr text_color = CLR_BLACK); ///< Ctor
176     //@}
177     void CompleteConstruction() override;
178 
179     /** \name Accessors */ ///@{
180     Pt                  MinUsableSize() const override;
181 
182     /** Returns button state \see ButtonState */
183     ButtonState         State() const;
184 
185     const std::string&  Text() const;        ///< Returns the label to be used as the button label
186 
187     bool                Checked() const;       ///< Returns true if button is checked
188 
189     TextControl*        GetLabel() const;
190 
191     mutable CheckedSignalType CheckedSignal; ///< The checked signal object for this StaticButton
192     //@}
193 
194     /** \name Mutators */ ///@{
195     void Show() override;
196     void Render() override;
197     void SizeMove(const Pt& ul, const Pt& lr) override;
198 
199     void Reset();                 ///< Unchecks button
200     void SetCheck(bool b = true); ///< (Un)checks button
201     void SetTextColor(Clr c); ///< Sets the color of the box label text
202     //@}
203 
204 protected:
205     /** \name Mutators */ ///@{
206     void LButtonDown(const Pt& pt, Flags<ModKey> mod_keys) override;
207     void LDrag(const Pt& pt, const Pt& move, Flags<ModKey> mod_keys) override;
208     void LButtonUp(const Pt& pt, Flags<ModKey> mod_keys) override;
209     void LClick(const Pt& pt, Flags<ModKey> mod_keys) override;
210     void MouseHere(const Pt& pt, Flags<ModKey> mod_keys) override;
211     void MouseLeave() override;
212 
213     /** Sets button state programmatically \see ButtonState */
214     void SetState(ButtonState next_state);
215     //@}
216 
217 private:
218     std::shared_ptr<StateButtonRepresenter> m_representer;
219     std::shared_ptr<TextControl>            m_label;       ///< Label used to display text
220     ButtonState                             m_state;       ///< Button is always in exactly one of the ButtonState states above
221     bool                                    m_checked;     ///< true when this button in a checked, active state
222 };
223 
224 
225 /** \brief Visual representation of a state button.
226 
227     The StateButtonRepresenter is a stub interface to implement a visual
228     representation of a StateButton instance.  A representer can be shared
229     amongst multiple instances of a StateButton, so no subclass should
230     store instance specific attributes of a state button.
231  */
232 class GG_API StateButtonRepresenter
233 {
234 public:
235     /** \brief Render the given state button according to its state.
236 
237         \param button The StateButton instance to render.
238      */
239     virtual void Render(const StateButton& button) const;
240 
241     /** \brief Respong to a button state change.
242 
243         This method is called whenever a Button chanes its state.
244 
245         \param button  The button that changed its state and is in the most
246                        recent state.
247         \param previous_state  The previous button state.
248      */
249     virtual void OnChanged(const StateButton& button, StateButton::ButtonState previous_state) const;
250 
251     /** \brief Respond to a state button change.
252 
253         This method is called whenever a StateButton changes its
254         state.
255 
256         \param checked True if the state button was checked, False
257                        if not.
258      */
259     virtual void OnChecked(bool checked) const;
260 
261     /** \brief Return the minimum size required for a usable representation
262 
263         \param button The state button to calculate the minimum size of.
264         \return The minimum size to create a usable representation of the
265            given state button.
266      */
267     virtual Pt MinUsableSize(const StateButton& button) const;
268 
269 protected:
270     /** \brief Calculate the layout of text and button component.
271 
272         \param button The StateButton instance to layout.
273         \param button_ul[out] The upper left corner of the visual
274                button.
275         \param button_lr[out] The lower right corner of the visual
276                button.
277         \param text_ul[out] The upper left corner of the button
278                label.
279      */
280     virtual void DoLayout(const GG::StateButton& button, Pt& button_ul, Pt& button_lr, Pt& text_ul) const;
281 };
282 
283 
284 /** \brief Builtin representation of a check box state button. */
285 class GG_API BeveledCheckBoxRepresenter: public StateButtonRepresenter
286 {
287 public:
288     explicit BeveledCheckBoxRepresenter(Clr int_color = CLR_ZERO);
289 
290     void Render(const StateButton& button) const override;
291 
292 private:
293     Clr m_int_color;
294 };
295 
296 
297 /** \brief Builtin representation of a radio state button. */
298 class GG_API BeveledRadioRepresenter: public StateButtonRepresenter
299 {
300 public:
301     explicit BeveledRadioRepresenter(Clr int_color = CLR_ZERO);
302 
303     void Render(const StateButton& button) const override;
304 
305 private:
306     Clr m_int_color;
307 };
308 
309 
310 /** \brief Builtin representation of a tab state button (part of the TabWnd). */
311 class GG_API BeveledTabRepresenter: public StateButtonRepresenter
312 {
313 public:
314     void Render(const StateButton& button) const override;
315 
316     Pt MinUsableSize(const StateButton& button) const override;
317 };
318 
319 
320 /** \brief This is a class that encapsulates multiple GG::StateButtons into a
321     single radio-button control.
322 
323     RadioButtonGroup emits a signal whenever its currently-checked button
324     changes.  The signal indicates which button has been pressed, by passing
325     the index of the button; the currently-checked button index is NO_BUTTON
326     when no button is checked.  Any StateButton-derived controls can be used
327     in a RadioButtonGroup. */
328 class GG_API RadioButtonGroup : public Control
329 {
330 public:
331     /** \name Signal Types */ ///@{
332     /** emitted when the currently-selected button has changed; the new
333         selected button's index in the group is provided (this may be
334         NO_BUTTON if no button is currently selected) */
335     typedef boost::signals2::signal<void (std::size_t)> ButtonChangedSignalType;
336     //@}
337 
338     /** \name Structors */ ///@{
339     RadioButtonGroup(Orientation orientation);
340     //@}
341 
342     /** \name Accessors */ ///@{
343     Pt MinUsableSize() const override;
344 
345     /** Returns the orientation of the buttons in the group */
346     Orientation      GetOrientation() const;
347 
348     /** Returns true iff NumButtons() == 0 */
349     bool             Empty() const;
350 
351     /** Returns the number of buttons in this control */
352     std::size_t      NumButtons() const;
353 
354     /** Returns the index of the currently checked button, or NO_BUTTON if
355         none are checked */
356     std::size_t      CheckedButton() const;
357 
358     /** Returns true iff the buttons in the group are to be expanded to fill
359         the group's available space.  If false, this indicates that the
360         buttons are to be spaced out evenly, and that they should all be their
361         MinUsableSize()s. */
362     bool             ExpandButtons() const;
363 
364     /** Returns true iff the buttons in the group are to be expanded in
365         proportion to their initial sizes.  If false, this indicates that the
366         buttons are to be expanded evenly.  Note that this has no effect if
367         ExpandButtons() is false. */
368     bool             ExpandButtonsProportionally() const;
369 
370     /** Returns true iff this button group will render an outline of itself;
371         this is sometimes useful for debugging purposes */
372     bool             RenderOutline() const;
373 
374     mutable ButtonChangedSignalType ButtonChangedSignal; ///< The button changed signal object for this RadioButtonGroup
375     //@}
376 
377     /** \name Mutators */ ///@{
378     void Render() override;
379 
380     /** Checks the index-th button, and unchecks all others.  If there is no
381         index-th button, they are all unchecked, and the currently-checked
382         button index is set to NO_BUTTON. */
383     void SetCheck(std::size_t index);
384 
385     /** Disables (with b == true) or enables (with b == false) the index-th
386         button, if it exists.  If the button exists, is being disabled, and is
387         the one currently checked, the currently-checked button index is set
388         to NO_BUTTON. */
389     void DisableButton(std::size_t index, bool b = true);
390 
391     /** Adds a button to the end of the group. The button group owns \p bn.*/
392     void AddButton(std::shared_ptr<StateButton> bn);
393 
394     /** Adds a button to the group at position \a index.  \a index must be in
395         the range [0, NumButtons()]. The button group owns \p bn.*/
396     void InsertButton(std::size_t index, std::shared_ptr<StateButton> bn);
397 
398     /** Removes \a button from the group.  If \a button is at index i, and is
399         the currently-checked button, the currently-checked button index is
400         set to NO_BUTTON.  If the currently-checked button is after i, the
401         currently-checked button index will be decremented.  In either case, a
402         ButtonChangedSignal will be emitted.  Note that this causes the layout
403         to relinquish responsibility for \a wnd's memory management. */
404     void RemoveButton(StateButton* button);
405 
406     /** Set this to true if the buttons in the group are to be expanded to
407         fill the group's available space.  If set to false, the buttons are to
408         be spaced out evenly, and they will all be at least their
409         MinUsableSize()s. */
410     void ExpandButtons(bool expand);
411 
412     /** Set this to true if the buttons in the group are to be expanded in
413         proportion to their initial sizes.  If set to false, this indicates
414         that the buttons are to be expanded evenly.  Note that this has no
415         effect if ExpandButtons() is false. */
416     void ExpandButtonsProportionally(bool proportional);
417 
418     /** Set this to true if this button group should render an outline of
419         itself; this is sometimes useful for debugging purposes */
420     void RenderOutline(bool render_outline);
421 
422     /** Raises the currently-selected button to the top of the child z-order.
423         If there is no currently-selected button, no action is taken. */
424     void RaiseCheckedButton();
425 
426     /** The invalid button position index that there is no currently-checked
427         button. */
428     static const std::size_t NO_BUTTON;
429     //@}
430 
431 protected:
432     /** \brief Encapsulates all data pertaining ot a single button in a
433         RadioButtonGroup. */
434     struct GG_API ButtonSlot
435     {
436         ButtonSlot(std::shared_ptr<StateButton>& button_);
437 
438         std::shared_ptr<StateButton> button;
439 
440         boost::signals2::connection connection;
441     };
442 
443     /** \name Accessors */ ///@{
444     const std::vector<ButtonSlot>& ButtonSlots() const; ///< returns the state buttons in the group
445     //@}
446 
447 private:
448     void ConnectSignals();
449     void SetCheckImpl(std::size_t index, bool signal);
450     void Reconnect();
451 
452     const Orientation       m_orientation;
453     std::vector<ButtonSlot> m_button_slots;
454     std::size_t             m_checked_button; ///< the index of the currently-checked button; NO_BUTTON if none is clicked
455     bool                    m_expand_buttons;
456     bool                    m_expand_buttons_proportionally;
457     bool                    m_render_outline;
458 };
459 
460 } // namespace GG
461 
462 #endif
463