1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 //==============================================================================
30 /**
31     A resizable window with a title bar and maximise, minimise and close buttons.
32 
33     This subclass of ResizableWindow creates a fairly standard type of window with
34     a title bar and various buttons. The name of the component is shown in the
35     title bar, and an icon can optionally be specified with setIcon().
36 
37     All the methods available to a ResizableWindow are also available to this,
38     so it can easily be made resizable, minimised, maximised, etc.
39 
40     It's not advisable to add child components directly to a DocumentWindow: put them
41     inside your content component instead. And overriding methods like resized(), moved(), etc
42     is also not recommended - instead override these methods for your content component.
43     (If for some obscure reason you do need to override these methods, always remember to
44     call the super-class's resized() method too, otherwise it'll fail to lay out the window
45     decorations correctly).
46 
47     You can also automatically add a menu bar to the window, using the setMenuBar()
48     method.
49 
50     @see ResizableWindow, DialogWindow
51 
52     @tags{GUI}
53 */
54 class JUCE_API  DocumentWindow   : public ResizableWindow
55 {
56 public:
57     //==============================================================================
58     /** The set of available button-types that can be put on the title bar.
59 
60         @see setTitleBarButtonsRequired
61     */
62     enum TitleBarButtons
63     {
64         minimiseButton = 1,
65         maximiseButton = 2,
66         closeButton = 4,
67 
68         /** A combination of all the buttons above. */
69         allButtons = 7
70     };
71 
72     //==============================================================================
73     /** Creates a DocumentWindow.
74 
75         @param name             the name to give the component - this is also
76                                 the title shown at the top of the window. To change
77                                 this later, use setName()
78         @param backgroundColour the colour to use for filling the window's background.
79         @param requiredButtons  specifies which of the buttons (close, minimise, maximise)
80                                 should be shown on the title bar. This value is a bitwise
81                                 combination of values from the TitleBarButtons enum. Note
82                                 that it can be "allButtons" to get them all. You
83                                 can change this later with the setTitleBarButtonsRequired()
84                                 method, which can also specify where they are positioned.
85         @param addToDesktop     if true, the window will be automatically added to the
86                                 desktop; if false, you can use it as a child component
87         @see TitleBarButtons
88     */
89     DocumentWindow (const String& name,
90                     Colour backgroundColour,
91                     int requiredButtons,
92                     bool addToDesktop = true);
93 
94     /** Destructor.
95         If a content component has been set with setContentOwned(), it will be deleted.
96     */
97     ~DocumentWindow() override;
98 
99     //==============================================================================
100     /** Changes the component's name.
101 
102         (This is overridden from Component::setName() to cause a repaint, as
103         the name is what gets drawn across the window's title bar).
104     */
105     void setName (const String& newName) override;
106 
107     /** Sets an icon to show in the title bar, next to the title.
108 
109         A copy is made internally of the image, so the caller can delete the
110         image after calling this. If an empty Image is passed-in, any existing icon
111         will be removed.
112     */
113     void setIcon (const Image& imageToUse);
114 
115     /** Changes the height of the title-bar. */
116     void setTitleBarHeight (int newHeight);
117 
118     /** Returns the current title bar height. */
119     int getTitleBarHeight() const;
120 
121     /** Changes the set of title-bar buttons being shown.
122 
123         @param requiredButtons  specifies which of the buttons (close, minimise, maximise)
124                                 should be shown on the title bar. This value is a bitwise
125                                 combination of values from the TitleBarButtons enum. Note
126                                 that it can be "allButtons" to get them all.
127         @param positionTitleBarButtonsOnLeft    if true, the buttons should go at the
128                                 left side of the bar; if false, they'll be placed at the right
129     */
130     void setTitleBarButtonsRequired (int requiredButtons,
131                                      bool positionTitleBarButtonsOnLeft);
132 
133     /** Sets whether the title should be centred within the window.
134 
135         If true, the title text is shown in the middle of the title-bar; if false,
136         it'll be shown at the left of the bar.
137     */
138     void setTitleBarTextCentred (bool textShouldBeCentred);
139 
140     //==============================================================================
141     /** Creates a menu inside this window.
142 
143         @param menuBarModel     this specifies a MenuBarModel that should be used to
144                                 generate the contents of a menu bar that will be placed
145                                 just below the title bar, and just above any content
146                                 component. If this value is a nullptr, any existing menu bar
147                                 will be removed from the component; if it is not a nullptr,
148                                 one will be added if it's required.
149         @param menuBarHeight    the height of the menu bar component, if one is needed. Pass a value of zero
150                                 or less to use the look-and-feel's default size.
151     */
152     void setMenuBar (MenuBarModel* menuBarModel,
153                      int menuBarHeight = 0);
154 
155     /** Returns the current menu bar component, or null if there isn't one.
156         This is probably a MenuBarComponent, unless a custom one has been set using
157         setMenuBarComponent().
158     */
159     Component* getMenuBarComponent() const noexcept;
160 
161     /** Replaces the current menu bar with a custom component.
162         The component will be owned and deleted by the document window.
163     */
164     void setMenuBarComponent (Component* newMenuBarComponent);
165 
166     //==============================================================================
167     /** This method is called when the user tries to close the window.
168 
169         This is triggered by the user clicking the close button, or using some other
170         OS-specific key shortcut or OS menu for getting rid of a window.
171 
172         If the window is just a pop-up, you should override this closeButtonPressed()
173         method and make it delete the window in whatever way is appropriate for your
174         app. E.g. you might just want to call "delete this".
175 
176         If your app is centred around this window such that the whole app should quit when
177         the window is closed, then you will probably want to use this method as an opportunity
178         to call JUCEApplicationBase::quit(), and leave the window to be deleted later by your
179         JUCEApplicationBase::shutdown() method. (Doing it this way means that your window will
180         still get cleaned-up if the app is quit by some other means (e.g. a cmd-Q on the mac
181         or closing it via the taskbar icon on Windows).
182 
183         (Note that the DocumentWindow class overrides Component::userTriedToCloseWindow() and
184         redirects it to call this method, so any methods of closing the window that are
185         caught by userTriedToCloseWindow() will also end up here).
186     */
187     virtual void closeButtonPressed();
188 
189     /** Callback that is triggered when the minimise button is pressed.
190 
191         The default implementation of this calls ResizableWindow::setMinimised(), but
192         you can override it to do more customised behaviour.
193     */
194     virtual void minimiseButtonPressed();
195 
196     /** Callback that is triggered when the maximise button is pressed, or when the
197         title-bar is double-clicked.
198 
199         The default implementation of this calls ResizableWindow::setFullScreen(), but
200         you can override it to do more customised behaviour.
201     */
202     virtual void maximiseButtonPressed();
203 
204     //==============================================================================
205     /** Returns the close button, (or nullptr if there isn't one). */
206     Button* getCloseButton() const noexcept;
207 
208     /** Returns the minimise button, (or nullptr if there isn't one). */
209     Button* getMinimiseButton() const noexcept;
210 
211     /** Returns the maximise button, (or nullptr if there isn't one). */
212     Button* getMaximiseButton() const noexcept;
213 
214     //==============================================================================
215     /** A set of colour IDs to use to change the colour of various aspects of the window.
216 
217         These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
218         methods.
219 
220         @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
221     */
222     enum ColourIds
223     {
224         textColourId                = 0x1005701,  /**< The colour to draw any text with. It's up to the look
225                                                        and feel class how this is used. */
226     };
227 
228     //==============================================================================
229     /** This abstract base class is implemented by LookAndFeel classes to provide
230         window drawing functionality.
231     */
232     struct JUCE_API  LookAndFeelMethods
233     {
234         virtual ~LookAndFeelMethods() = default;
235 
236         virtual void drawDocumentWindowTitleBar (DocumentWindow&,
237                                                  Graphics&, int w, int h,
238                                                  int titleSpaceX, int titleSpaceW,
239                                                  const Image* icon,
240                                                  bool drawTitleTextOnLeft) = 0;
241 
242         virtual Button* createDocumentWindowButton (int buttonType) = 0;
243 
244         virtual void positionDocumentWindowButtons (DocumentWindow&,
245                                                     int titleBarX, int titleBarY, int titleBarW, int titleBarH,
246                                                     Button* minimiseButton,
247                                                     Button* maximiseButton,
248                                                     Button* closeButton,
249                                                     bool positionTitleBarButtonsOnLeft) = 0;
250     };
251 
252     //==============================================================================
253    #ifndef DOXYGEN
254     /** @internal */
255     void paint (Graphics&) override;
256     /** @internal */
257     void resized() override;
258     /** @internal */
259     void lookAndFeelChanged() override;
260     /** @internal */
261     BorderSize<int> getBorderThickness() override;
262     /** @internal */
263     BorderSize<int> getContentComponentBorder() override;
264     /** @internal */
265     void mouseDoubleClick (const MouseEvent&) override;
266     /** @internal */
267     void userTriedToCloseWindow() override;
268     /** @internal */
269     void activeWindowStatusChanged() override;
270     /** @internal */
271     int getDesktopWindowStyleFlags() const override;
272     /** @internal */
273     void parentHierarchyChanged() override;
274     /** @internal */
275     Rectangle<int> getTitleBarArea();
276    #endif
277 
278 private:
279     //==============================================================================
280     int titleBarHeight = 26, menuBarHeight = 24, requiredButtons;
281     bool positionTitleBarButtonsOnLeft, drawTitleTextCentred = true;
282     std::unique_ptr<Button> titleBarButtons [3];
283     Image titleBarIcon;
284     std::unique_ptr<Component> menuBar;
285     MenuBarModel* menuBarModel = nullptr;
286 
287     class ButtonListenerProxy;
288     std::unique_ptr<ButtonListenerProxy> buttonListener;
289 
290     void repaintTitleBar();
291 
292     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DocumentWindow)
293 };
294 
295 } // namespace juce
296