1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2004-2009  The Mana World Development Team
4  *  Copyright (C) 2009-2010  The Mana Developers
5  *  Copyright (C) 2011-2019  The ManaPlus Developers
6  *  Copyright (C) 2019-2021  Andrei Karas
7  *
8  *  This file is part of The ManaPlus Client.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 /*      _______   __   __   __   ______   __   __   _______   __   __
25  *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
26  *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
27  *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
28  *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
29  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
30  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
31  *
32  * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
33  *
34  *
35  * Per Larsson a.k.a finalman
36  * Olof Naessén a.k.a jansem/yakslem
37  *
38  * Visit: http://guichan.sourceforge.net
39  *
40  * License: (BSD)
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in
48  *    the documentation and/or other materials provided with the
49  *    distribution.
50  * 3. Neither the name of Guichan nor the names of its contributors may
51  *    be used to endorse or promote products derived from this software
52  *    without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
56  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
57  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
58  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
60  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
61  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
62  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
63  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65  */
66 
67 #ifndef GUI_WIDGETS_WINDOW_H
68 #define GUI_WIDGETS_WINDOW_H
69 
70 #include "enums/simpletypes/modal.h"
71 #include "enums/simpletypes/move.h"
72 
73 #include "enums/resources/imageposition.h"
74 
75 #include "gui/fonts/textchunk.h"
76 
77 #include "gui/widgets/basiccontainer2.h"
78 
79 #include "listeners/mouselistener.h"
80 #include "listeners/widgetlistener.h"
81 
82 #include "render/graphics.h"
83 
84 #include "localconsts.h"
85 
86 class ContainerPlacer;
87 class ImageCollection;
88 class Layout;
89 class LayoutCell;
90 class Skin;
91 class WindowContainer;
92 
93 /**
94  * A window. This window can be dragged around and has a title bar. Windows are
95  * invisible by default.
96  *
97  * \ingroup GUI
98  */
99 class Window notfinal : public BasicContainer2,
100                         public MouseListener,
101                         public WidgetListener
102 {
103     public:
104         /**
105          * Constructor. Initializes the title to the given text and hooks
106          * itself into the window container.
107          *
108          * @param caption The initial window title, "Window" by default.
109          * @param modal   Block input to other windows.
110          * @param parent  The parent window. This is the window standing above
111          *                this one in the window hiearchy. When reordering,
112          *                a window will never go below its parent window.
113          * @param skin    The location where the window's skin XML can be found.
114          */
115         Window(const std::string &caption,
116                const Modal modal,
117                Window *const parent,
118                std::string skin);
119 
120         A_DELETE_COPY(Window)
121 
122         /**
123          * Destructor. Deletes all the added widgets.
124          */
125         ~Window() override;
126 
127         /**
128          * Sets the window container to be used by new windows.
129          */
130         static void setWindowContainer(WindowContainer *const windowContainer);
131 
132         /**
133          * Draws the window.
134          */
135         void draw(Graphics *const graphics) override A_NONNULL(2);
136 
137         void safeDraw(Graphics *const graphics) override A_NONNULL(2);
138 
139         /**
140          * Sets the size of this window.
141          */
142         void setContentSize(int width, int height);
143 
144         /**
145          * Sets the location relative to the given widget.
146          */
147         void setLocationRelativeTo(const Widget *const widget);
148 
149         /**
150          * Sets the location relative to the given widget (only horisontally)
151          */
152         void setLocationHorisontallyRelativeTo(const Widget
153                                                *const widget);
154 
155         /**
156          * Sets the location relative to the given enumerated position.
157          */
158         void setLocationRelativeTo(const ImagePosition::Type &position,
159                                    int offsetX,
160                                    int offsetY);
161 
162         /**
163          * Sets whether or not the window can be resized.
164          */
165         void setResizable(const bool resize);
166 
167         void redraw();
168 
169         /**
170          * Called whenever the widget changes size.
171          */
172         void widgetResized(const Event &event) override;
173 
174         void widgetMoved(const Event& event) override;
175 
176         /**
177          * Called whenever the widget is hidden.
178          */
179         void widgetHidden(const Event &event) override;
180 
181         /**
182          * Sets whether or not the window has a close button.
183          */
184         void setCloseButton(const bool flag);
185 
getCloseButton()186         bool getCloseButton() const noexcept2 A_WARN_UNUSED
187         { return mCloseWindowButton; }
188 
setAllowClose(const bool b)189         void setAllowClose(const bool b)
190         { mAllowClose = b; }
191 
getAlowClose()192         bool getAlowClose() const noexcept2 A_WARN_UNUSED
193         { return mCloseWindowButton || mAllowClose; }
194 
195         /**
196          * Returns whether the window can be resized.
197          */
198         bool isResizable() const A_WARN_UNUSED;
199 
200         /**
201          * Sets the minimum width of the window.
202          */
203         void setMinWidth(const int width);
204 
getMinWidth()205         int getMinWidth() const noexcept2 A_WARN_UNUSED
206         { return mMinWinWidth; }
207 
208         /**
209          * Sets the minimum height of the window.
210          */
211         void setMinHeight(const int height);
212 
getMinHeight()213         int getMinHeight() const noexcept2 A_WARN_UNUSED
214         { return mMinWinHeight; }
215 
216         /**
217          * Sets the maximum width of the window.
218          */
219         void setMaxWidth(const int width);
220 
getMaxWidth()221         int getMaxWidth() const noexcept2 A_WARN_UNUSED
222         { return mMaxWinWidth; }
223 
224         /**
225          * Sets the minimum height of the window.
226          */
227         void setMaxHeight(const int height);
228 
getMaxHeight()229         int getMaxHeight() const noexcept2 A_WARN_UNUSED
230         { return mMaxWinHeight; }
231 
232         /**
233          * Sets flag to show a title or not.
234          */
setShowTitle(bool flag)235         void setShowTitle(bool flag)
236         { mShowTitle = flag; }
237 
238         /**
239          * Sets whether or not the window has a sticky button.
240          */
241         void setStickyButton(const bool flag);
242 
243         /**
244           * Sets whether the window is sticky. A sticky window will not have
245           * its visibility set to false on a general setVisible(false) call.
246           * Use this to set the default before you call loadWindowState().
247           */
248         void setSticky(const bool sticky);
249 
250         /**
251          * Returns whether the window is sticky.
252          */
isSticky()253         bool isSticky() const noexcept2 A_WARN_UNUSED
254         { return mSticky; }
255 
256         /**
257           * Sets whether the window sticky mean window locked or not.
258           */
259         void setStickyButtonLock(const bool sticky);
260 
261         /**
262          * Returns whether the window sticky locking window.
263          */
isStickyButtonLock()264         bool isStickyButtonLock() const noexcept2 A_WARN_UNUSED
265         { return mStickyButtonLock; }
266 
267         /**
268          * Overloads window setVisible by Guichan to allow sticky window
269          * handling.
270          */
271         virtual void setVisible(Visible visible);
272 
273         /**
274          * Overloads window setVisible by Guichan to allow sticky window
275          * handling, or not, if you force the sticky state.
276          */
277         void setVisible(const Visible visible, const bool forceSticky);
278 
279         /**
280          * Returns whether the window is visible by default.
281          */
isDefaultVisible()282         bool isDefaultVisible() const noexcept2 A_WARN_UNUSED
283         { return mDefaultVisible; }
284 
285         /**
286          * Sets whether the window is visible by default.
287          */
setDefaultVisible(const bool save)288         void setDefaultVisible(const bool save)
289         { mDefaultVisible = save; }
290 
291         /**
292          * Returns whether the window will save it's visibility.
293          */
willSaveVisible()294         bool willSaveVisible() const
295         { return mSaveVisible; }
296 
297         /**
298          * Sets whether the window will save it's visibility.
299          */
setSaveVisible(const bool save)300         void setSaveVisible(const bool save)
301         { mSaveVisible = save; }
302 
303         void postInit() override;
304 
305         /**
306          * Returns the parent window.
307          *
308          * @return The parent window or <code>NULL</code> if there is none.
309          */
getParentWindow()310         Window *getParentWindow() const
311         { return mParentWindow; }
312 
313         /**
314          * Schedule this window for deletion. It will be deleted at the start
315          * of the next logic update.
316          */
317         virtual void scheduleDelete();
318 
319         /**
320          * Starts window resizing when appropriate.
321          */
322         void mousePressed(MouseEvent &event) override;
323 
324         /**
325          * Implements window resizing and makes sure the window is not
326          * dragged/resized outside of the screen.
327          */
328         void mouseDragged(MouseEvent &event) override;
329 
330         /**
331          * Implements custom cursor image changing context, based on mouse
332          * relative position.
333          */
334         void mouseMoved(MouseEvent &event) override;
335 
336         /**
337          * When the mouse button has been let go, this ensures that the mouse
338          * custom cursor is restored back to it's standard image.
339          */
340         void mouseReleased(MouseEvent &event) override;
341 
342         /**
343          * When the mouse leaves the window this ensures that the custom cursor
344          * is restored back to it's standard image.
345          */
346         void mouseExited(MouseEvent &event) override;
347 
348         void mouseEntered(MouseEvent &event) override;
349 
350         void updateResizeHandler(MouseEvent &event);
351 
352         /**
353          * Sets the name of the window. This is not the window title.
354          */
setWindowName(const std::string & name)355         void setWindowName(const std::string &name)
356         { mWindowName = name; }
357 
358         /**
359          * Returns the name of the window. This is not the window title.
360          */
getWindowName()361         const std::string &getWindowName() const noexcept2 A_WARN_UNUSED
362         { return mWindowName; }
363 
364         /**
365          * Reads the position (and the size for resizable windows) in the
366          * configuration based on the given string.
367          * Uses the default values when config values are missing.
368          * Don't forget to set these default values and resizable before
369          * calling this function.
370          */
371         void loadWindowState();
372 
373         /**
374          * Saves the window state so that when the window is reloaded, it'll
375          * maintain its previous state and location.
376          */
377         void saveWindowState();
378 
379         /**
380          * Set the default win pos and size.
381          * (which can be different of the actual ones.)
382          */
383         void setDefaultSize(const int defaultX, const int defaultY,
384                             int defaultWidth, int defaultHeight);
385 
386         /**
387          * Set the default win pos and size to the current ones.
388          */
389         void setDefaultSize();
390 
391         /**
392          * Set the default win pos and size.
393          * (which can be different of the actual ones.)
394          * This version of setDefaultSize sets the window's position based
395          * on a relative enumerated position, rather than a coordinate position.
396          */
397         void setDefaultSize(const int defaultWidth,
398                             const int defaultHeight,
399                             const ImagePosition::Type &position,
400                             const int offsetx,
401                             const int offsetY);
402 
403         /**
404          * Reset the win pos and size to default. Don't forget to set defaults
405          * first.
406          */
407         virtual void resetToDefaultSize();
408 
409         /**
410          * Adjusts the window position after the application window has been
411          * resized.
412          */
413         void adjustPositionAfterResize(const int oldScreenWidth,
414                                        const int oldScreenHeight);
415 
416         /**
417          * Gets the layout handler for this window.
418          */
419         Layout &getLayout() A_WARN_UNUSED;
420 
421         /**
422          * Clears the window's layout (useful for redesigning the window). Does
423          * not delete the widgets!
424          */
425         void clearLayout();
426 
427         /**
428          * Computes the position of the widgets according to the current
429          * layout. Resizes the window so that the layout fits. Deletes the
430          * layout.
431          * @param w if non-zero, force the window to this width.
432          * @param h if non-zero, force the window to this height.
433          * @note This function is meant to be called with fixed-size windows.
434          */
435         void reflowLayout(int w,
436                           int h);
437 
438         /**
439          * Adds a widget to the window and sets it at given cell.
440          */
441         LayoutCell &place(const int x,
442                           const int y,
443                           Widget *const wg,
444                           const int w,
445                           const int h);
446 
447         /**
448          * Returns a proxy for adding widgets in an inner table of the layout.
449          */
450         ContainerPlacer getPlacer(const int x, const int y) A_WARN_UNUSED;
451 
452         /**
453          * Positions the window in the center of it's parent.
454          */
455         void center();
456 
457         /**
458          * Positions the window in the horisontal center of it's parent.
459          */
460         void centerHorisontally();
461 
462         /**
463          * Overrideable functionality for when the window is to close. This
464          * allows for class implementations to clean up or do certain actions
465          * on window close they couldn't do otherwise.
466          */
467         virtual void close();
468 
469          /**
470          * Allows the windows modal status to change
471          */
472         void setModal(const Modal modal);
473 
474         Rect getWindowArea() const A_WARN_UNUSED;
475 
476         bool isResizeAllowed(const MouseEvent &event) const A_WARN_UNUSED;
477 
setCaptionFont(Font * font)478         void setCaptionFont(Font *font)
479         { mCaptionFont = font; }
480 
enableVisibleSound(bool b)481         void enableVisibleSound(bool b)
482         { mPlayVisibleSound = b; }
483 
isWindowVisible()484         bool isWindowVisible() const noexcept2 A_WARN_UNUSED
485         { return mVisible == Visible_true; }
486 
487         /**
488          * Sets the padding of the window. The padding is the distance between the
489          * window border and the content.
490          *
491          * @param padding The padding of the window.
492          * @see getPadding
493          */
setPadding(int padding)494         void setPadding(int padding)
495         { mPadding = padding; }
496 
497         /**
498          * Gets the padding of the window. The padding is the distance between the
499          * window border and the content.
500          *
501          * @return The padding of the window.
502          * @see setPadding
503          */
getPadding()504         int getPadding() const
505         { return mPadding; }
506 
507         /**
508          * Sets the title bar height.
509          *
510          * @param height The title height value.
511          * @see getTitleBarHeight
512          */
setTitleBarHeight(unsigned int height)513         void setTitleBarHeight(unsigned int height)
514         { mTitleBarHeight = height; }
515 
516         /**
517          * Gets the title bar height.
518          *
519          * @return The title bar height.
520          * @see setTitleBarHeight
521          */
getTitleBarHeight()522         unsigned int getTitleBarHeight() const
523         { return mTitleBarHeight; }
524 
525         /**
526          * Sets the caption of the window.
527          *
528          * @param caption The caption of the window.
529          * @see getCaption
530          */
setCaption(const std::string & caption)531         void setCaption(const std::string& caption)
532         { mCaption = caption; mTextChanged = true; }
533 
534         /**
535          * Gets the caption of the window.
536          *
537          * @return the caption of the window.
538          * @see setCaption
539          */
getCaption()540         const std::string& getCaption() const
541         { return mCaption; }
542 
543         /**
544          * Sets the alignment of the caption.
545          *
546          * @param alignment The alignment of the caption.
547          * @see getAlignment, Graphics
548          */
setAlignment(Graphics::Alignment alignment)549         void setAlignment(Graphics::Alignment alignment)
550         { mAlignment = alignment; }
551 
552         /**
553          * Gets the alignment of the caption.
554          *
555          * @return The alignment of caption.
556          * @see setAlignment, Graphics
557          */
getAlignment()558         Graphics::Alignment getAlignment() const
559         { return mAlignment; }
560 
561         /**
562          * Sets the window to be moveble or not.
563          *
564          * @param movable True if the window should be movable, false otherwise.
565          * @see isMovable
566          */
setMovable(Move movable)567         void setMovable(Move movable)
568         { mMovable = movable; }
569 
570         /**
571          * Checks if the window is movable.
572          *
573          * @return True if the window is movable, false otherwise.
574          * @see setMovable
575          */
isMovable()576         bool isMovable() const
577         { return mMovable == Move_true; }
578 
579         Rect getChildrenArea() override;
580 
581         /**
582          * Resizes the window to fit the content.
583          */
584         virtual void resizeToContent();
585 
586 #ifdef USE_PROFILER
587         virtual void logic();
588 #endif  // USE_PROFILER
589 
590     protected:
591         bool canMove() const A_WARN_UNUSED;
592 
593         int getOption(const std::string &name,
594                       const int def) const A_WARN_UNUSED;
595 
596         bool getOptionBool(const std::string &name,
597                            const bool def) const A_WARN_UNUSED;
598 
setTitlePadding(const int p)599         void setTitlePadding(const int p) noexcept2
600         { mTitlePadding = p; }
601 
getTitlePadding()602         int getTitlePadding() const noexcept2 A_WARN_UNUSED
603         { return mTitlePadding; }
604 
605         /**
606          * Holds the caption of the window.
607          */
608         std::string mCaption;
609 
610         /**
611          * Holds the alignment of the caption.
612          */
613         Graphics::Alignment mAlignment;
614 
615         /**
616          * Holds the padding of the window.
617          */
618         int mPadding;
619 
620         /**
621          * Holds the title bar height of the window.
622          */
623         unsigned int mTitleBarHeight;
624 
625         /**
626          * True if the window is movable, false otherwise.
627          */
628         Move mMovable;
629 
630         /**
631          * Holds a drag offset as an x coordinate where the drag of the window
632          * started if the window is being dragged. It's used to move the window
633          * correctly when dragged.
634          */
635         int mDragOffsetX;
636 
637         /**
638          * Holds a drag offset as an y coordinate where the drag of the window
639          * started if the window is being dragged. It's used to move the window
640          * correctly when dragged.
641          */
642         int mDragOffsetY;
643 
644         /**
645          * True if the window is being moved, false otherwise.
646          */
647         bool mMoved;
648 
649         Skin *mSkin;                  /**< Skin in use by this window */
650         int mDefaultX;                /**< Default window X position */
651         int mDefaultY;                /**< Default window Y position */
652         int mDefaultWidth;            /**< Default window width */
653         int mDefaultHeight;           /**< Default window height */
654         int mCaptionOffsetX;
655         int mCaptionOffsetY;
656         bool mShowTitle;              /**< Window has a title bar */
657         bool mLastRedraw;
658 
659     private:
660         enum ResizeHandles
661         {
662             TOP    = 0x01,
663             RIGHT  = 0x02,
664             BOTTOM = 0x04,
665             LEFT   = 0x08,
666             CLOSE  = 0x10
667         };
668 
669         /**
670          * Ensures the window is on the screen, moving it if necessary. This is
671          * used by loadWindowState and setVisible(true), and when the screen
672          * is resized.
673          */
674         void ensureOnScreen();
675 
676         void adjustSizeToScreen();
677 
678         /**
679          * Determines if the mouse is in a resize area and returns appropriate
680          * resize handles. Also initializes drag offset in case the resize
681          * grip is used.
682          *
683          * @see ResizeHandles
684          */
685         int getResizeHandles(const MouseEvent &event) A_WARN_UNUSED;
686 
687         Image *mGrip;                 /**< Resize grip */
688         Window *mParentWindow;        /**< The parent window */
689         Layout *mLayout;              /**< Layout handler */
690         Rect mCloseRect;    /**< Close button rectangle */
691         Rect mStickyRect;   /**< Sticky button rectangle */
692         Rect mGripRect;     /**< Resize grip rectangle */
693         TextChunk mTextChunk;
694         std::string mWindowName;      /**< Name of the window */
695         int mMinWinWidth;             /**< Minimum window width */
696         int mMinWinHeight;            /**< Minimum window height */
697         int mMaxWinWidth;             /**< Maximum window width */
698         int mMaxWinHeight;            /**< Maximum window height */
699 
700         static int mouseResize;       /**< Active resize handles */
701         static int windowInstances;   /**< Number of Window instances */
702 
703 
704         /**
705          * The width of the resize border. Is independent of the actual window
706          * border width, and determines mostly the size of the corner area
707          * where two borders are moved at the same time.
708          */
709         static const unsigned resizeBorderWidth = 10;
710         ImageCollection *mVertexes A_NONNULLPOINTER;
711         Graphics::Alignment mCaptionAlign;
712         int mTitlePadding;
713         int mGripPadding;
714         int mResizeHandles;
715         int mOldResizeHandles;
716         int mClosePadding;
717         int mStickySpacing;
718         int mStickyPadding;
719         Font *mCaptionFont A_NONNULLPOINTER;
720         Modal mModal;                 /**< Window is modal */
721         bool mCloseWindowButton;      /**< Window has a close button */
722         bool mDefaultVisible;         /**< Window's default visibility */
723         bool mSaveVisible;            /**< Window will save visibility */
724         bool mStickyButton;           /**< Window has a sticky button */
725         bool mSticky;                 /**< Window resists hiding*/
726         bool mStickyButtonLock;       /**< Window locked if sticky enabled*/
727         bool mPlayVisibleSound;
728         bool mInit;
729         bool mTextChanged;
730         bool mAllowClose;
731 };
732 
733 #endif  // GUI_WIDGETS_WINDOW_H
734