DISTRHO Plugin Framework
Window.hpp
1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
6  * or without fee is hereby granted, provided that the above copyright notice and this
7  * permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #ifndef DGL_WINDOW_HPP_INCLUDED
18 #define DGL_WINDOW_HPP_INCLUDED
19 
20 #include "Geometry.hpp"
21 
22 START_NAMESPACE_DGL
23 
24 class Application;
25 class TopLevelWidget;
26 
27 // -----------------------------------------------------------------------
28 
29 /**
30  DGL Window class.
31 
32  This is the where all OS-related events initially happen, before being propagated to any widgets.
33 
34  A Window MUST have an Application instance tied to it.
35  It is not possible to swap Application instances from within the lifetime of a Window.
36  But it is possible to completely change the Widgets that a Window contains during its lifetime.
37 
38  Typically the event handling functions as following:
39  Application -> Window -> Top-Level-Widget -> SubWidgets
40 
41  Please note that, unlike many other graphical toolkits out there,
42  DGL makes a clear distinction between a Window and a Widget.
43  You cannot directly draw in a Window, you need to create a Widget for that.
44 
45  Also, a Window MUST have a single top-level Widget.
46  The Window will take care of global screen positioning and resizing, everything else is sent for widgets to handle.
47 
48  ...
49  */
50 class Window
51 {
52  struct PrivateData;
53 
54 public:
55 #ifndef DGL_FILE_BROWSER_DISABLED
56  /**
57  File browser options.
58  @see Window::openFileBrowser
59  */
61  /**
62  File browser button state.
63  This allows to customize the behaviour of the file browse dialog buttons.
64  Note these are merely hints, not all systems support them.
65  */
66  enum ButtonState {
67  kButtonInvisible,
68  kButtonVisibleUnchecked,
69  kButtonVisibleChecked,
70  };
71 
72  /** Start directory, uses current working directory if null */
73  const char* startDir;
74  /** File browser dialog window title, uses "FileBrowser" if null */
75  const char* title;
76  // TODO file filter
77 
78  /**
79  File browser buttons.
80  */
81  struct Buttons {
82  /** Whether to list all files vs only those with matching file extension */
84  /** Whether to show hidden files */
86  /** Whether to show list of places (bookmarks) */
88 
89  /** Constructor for default values */
91  : listAllFiles(kButtonVisibleChecked),
92  showHidden(kButtonVisibleUnchecked),
93  showPlaces(kButtonVisibleChecked) {}
94  } buttons;
95 
96  /** Constructor for default values */
98  : startDir(nullptr),
99  title(nullptr),
100  buttons() {}
101  };
102 #endif // DGL_FILE_BROWSER_DISABLED
103 
104  /**
105  Window graphics context as a scoped struct.
106  This class gives graphics context drawing time to a window's widgets.
107  Typically used for allowing OpenGL drawing operations during a window + widget constructor.
108 
109  Unless you are subclassing the Window or StandaloneWindow classes, you do not need to care.
110  In such cases you will need to use this struct as a way to get a valid OpenGL context.
111  For example in a standalone application:
112  ```
113  int main()
114  {
115  Application app;
116  Window win(app);
117  ScopedPointer<MyCustomTopLevelWidget> widget;
118  {
119  const Window::ScopedGraphicsContext sgc(win);
120  widget = new MyCustomTopLevelWidget(win);
121  }
122  app.exec();
123  return 0;
124  }
125  ```
126 
127  This struct is necessary because we cannot automatically make the window leave the OpenGL context in custom code.
128  And we must always cleanly enter and leave the OpenGL context.
129  So in order to avoid messing up the global host context, this class is used around widget creation.
130  */
132  {
133  /** Constructor that will make the @a window graphics context the current one */
134  explicit ScopedGraphicsContext(Window& window);
135 
136  /** Overloaded constructor, gives back context to its transient parent when done */
137  explicit ScopedGraphicsContext(Window& window, Window& transientParentWindow);
138 
139  /** Desstructor for clearing current context, if not done yet */
141 
142  /** Early context clearing, useful for standalone windows not created by you. */
143  void done();
144 
145  DISTRHO_DECLARE_NON_COPYABLE(ScopedGraphicsContext)
146  DISTRHO_PREVENT_HEAP_ALLOCATION
147 
148  private:
149  Window& window;
150  Window::PrivateData* ppData;
151  bool active;
152  };
153 
154  /**
155  Constructor for a regular, standalone window.
156  */
157  explicit Window(Application& app);
158 
159  /**
160  Constructor for a modal window, by having another window as its transient parent.
161  The Application instance must be the same between the 2 windows.
162  */
163  explicit Window(Application& app, Window& transientParentWindow);
164 
165  /**
166  Constructor for an embed Window without known size,
167  typically used in modules or plugins that run inside another host.
168  */
169  explicit Window(Application& app,
170  uintptr_t parentWindowHandle,
171  double scaleFactor,
172  bool resizable);
173 
174  /**
175  Constructor for an embed Window with known size,
176  typically used in modules or plugins that run inside another host.
177  */
178  explicit Window(Application& app,
179  uintptr_t parentWindowHandle,
180  uint width,
181  uint height,
182  double scaleFactor,
183  bool resizable);
184 
185  /**
186  Destructor.
187  */
188  virtual ~Window();
189 
190  /**
191  Whether this Window is embed into another (usually not DGL-controlled) Window.
192  */
193  bool isEmbed() const noexcept;
194 
195  /**
196  Check if this window is visible / mapped.
197  Invisible windows do not receive events except resize.
198  @see setVisible(bool)
199  */
200  bool isVisible() const noexcept;
201 
202  /**
203  Set window visible (or not) according to @a visible.
204  Only valid for standalones, embed windows are always visible.
205  @see isVisible(), hide(), show()
206  */
207  void setVisible(bool visible);
208 
209  /**
210  Show window.
211  This is the same as calling setVisible(true).
212  @see isVisible(), setVisible(bool)
213  */
214  void show();
215 
216  /**
217  Hide window.
218  This is the same as calling setVisible(false).
219  @see isVisible(), setVisible(bool)
220  */
221  void hide();
222 
223  /**
224  Hide window and notify application of a window close event.
225  The application event-loop will stop when all windows have been closed.
226  For standalone windows only, has no effect if window is embed.
227  @see isEmbed()
228 
229  @note It is possible to hide the window while not stopping the event-loop.
230  A closed window is always hidden, but the reverse is not always true.
231  */
232  void close();
233 
234  /**
235  Check if this window is resizable (by the user or window manager).
236  @see setResizable
237  */
238  bool isResizable() const noexcept;
239 
240  /**
241  Set window as resizable (by the user or window manager).
242  It is always possible to resize a window programmatically, which is not the same as the user being allowed to it.
243  @note This function does nothing for plugins, where the resizable state is set via macro.
244  @see DISTRHO_UI_USER_RESIZABLE
245  */
246  void setResizable(bool resizable);
247 
248  /**
249  Get width.
250  */
251  uint getWidth() const noexcept;
252 
253  /**
254  Get height.
255  */
256  uint getHeight() const noexcept;
257 
258  /**
259  Get size.
260  */
261  Size<uint> getSize() const noexcept;
262 
263  /**
264  Set width.
265  */
266  void setWidth(uint width);
267 
268  /**
269  Set height.
270  */
271  void setHeight(uint height);
272 
273  /**
274  Set size using @a width and @a height values.
275  */
276  void setSize(uint width, uint height);
277 
278  /**
279  Set size.
280  */
281  void setSize(const Size<uint>& size);
282 
283  /**
284  Get the title of the window previously set with setTitle().
285  */
286  const char* getTitle() const noexcept;
287 
288  /**
289  Set the title of the window, typically displayed in the title bar or in window switchers.
290 
291  This only makes sense for non-embedded windows.
292  */
293  void setTitle(const char* title);
294 
295  /**
296  Check if key repeat events are ignored.
297  */
298  bool isIgnoringKeyRepeat() const noexcept;
299 
300  /**
301  Set to ignore (or not) key repeat events according to @a ignore.
302  */
303  void setIgnoringKeyRepeat(bool ignore) noexcept;
304 
305  /**
306  Add a callback function to be triggered on every idle cycle or on a specific timer frequency.
307  You can add more than one, and remove them at anytime with removeIdleCallback().
308  This can be used to perform some action at a regular interval with relatively low frequency.
309 
310  If providing a timer frequency, there are a few things to note:
311  1. There is a platform-specific limit to the number of supported timers, and overhead associated with each,
312  so you should create only a few timers and perform several tasks in one if necessary.
313  2. This timer frequency is not guaranteed to have a resolution better than 10ms
314  (the maximum timer resolution on Windows) and may be rounded up if it is too short.
315  On X11 and MacOS, a resolution of about 1ms can usually be relied on.
316  */
317  bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0);
318 
319  /**
320  Remove an idle callback previously added via addIdleCallback().
321  */
322  bool removeIdleCallback(IdleCallback* callback);
323 
324  /**
325  Get the application associated with this window.
326  */
327  Application& getApp() const noexcept;
328 
329  /**
330  Get the graphics context associated with this window.
331  GraphicsContext is an empty struct and needs to be casted into a different type in order to be usable,
332  for example GraphicsContext.
333  @see CairoSubWidget, CairoTopLevelWidget
334  */
335  const GraphicsContext& getGraphicsContext() const noexcept;
336 
337  /**
338  Get the "native" window handle.
339  Returned value depends on the platform:
340  - HaikuOS: This is a pointer to a `BView`.
341  - MacOS: This is a pointer to an `NSView*`.
342  - Windows: This is a `HWND`.
343  - Everything else: This is an [X11] `Window`.
344  */
345  uintptr_t getNativeWindowHandle() const noexcept;
346 
347  /**
348  Get the scale factor requested for this window.
349  This is purely informational, and up to developers to choose what to do with it.
350 
351  If you do not want to deal with this yourself,
352  consider using setGeometryConstraints() where you can specify to automatically scale the window contents.
353  @see setGeometryConstraints
354  */
355  double getScaleFactor() const noexcept;
356 
357  /**
358  Grab the keyboard input focus.
359  */
360  void focus();
361 
362 #ifndef DGL_FILE_BROWSER_DISABLED
363  /**
364  Open a file browser dialog with this window as parent.
365  A few options can be specified to setup the dialog.
366 
367  If a path is selected, onFileSelected() will be called with the user chosen path.
368  If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename.
369 
370  This function does not block the event loop.
371  */
372  bool openFileBrowser(const FileBrowserOptions& options = FileBrowserOptions());
373 #endif
374 
375  /**
376  Request repaint of this window, for the entire area.
377  */
378  void repaint() noexcept;
379 
380  /**
381  Request partial repaint of this window, with bounds according to @a rect.
382  */
383  void repaint(const Rectangle<uint>& rect) noexcept;
384 
385  /**
386  Render this window's content into a picture file, specified by @a filename.
387  Window must be visible and on screen.
388  Written picture format is PPM.
389  */
390  void renderToPicture(const char* filename);
391 
392  /**
393  Run this window as a modal, blocking input events from the parent.
394  Only valid for windows that have been created with another window as parent (as passed in the constructor).
395  Can optionally block-wait, but such option is only available if the application is running as standalone.
396  */
397  void runAsModal(bool blockWait = false);
398 
399  /**
400  Get the geometry constraints set for the Window.
401  @see setGeometryConstraints
402  */
403  Size<uint> getGeometryConstraints(bool& keepAspectRatio);
404 
405  /**
406  Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically.
407  */
408  void setGeometryConstraints(uint minimumWidth,
409  uint minimumHeight,
410  bool keepAspectRatio = false,
411  bool automaticallyScale = false);
412 
413  /** DEPRECATED Use isIgnoringKeyRepeat(). */
414  DISTRHO_DEPRECATED_BY("isIgnoringKeyRepeat()")
415  inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); }
416 
417  /** DEPRECATED Use getScaleFactor(). */
418  DISTRHO_DEPRECATED_BY("getScaleFactor()")
419  inline double getScaling() const noexcept { return getScaleFactor(); }
420 
421  /** DEPRECATED Use runAsModal(bool). */
422  DISTRHO_DEPRECATED_BY("runAsModal(bool)")
423  inline void exec(bool blockWait = false) { runAsModal(blockWait); }
424 
425 protected:
426  /**
427  A function called when the window is attempted to be closed.
428  Returning true closes the window, which is the default behaviour.
429  Override this method and return false to prevent the window from being closed by the user.
430 
431  This method is not used for embed windows, and not even made available in DISTRHO_NAMESPACE::UI.
432  For embed windows, closing is handled by the host/parent process and we have no control over it.
433  As such, a close action on embed windows will always succeed and cannot be cancelled.
434 
435  NOTE: This currently does not work under macOS.
436  */
437  virtual bool onClose();
438 
439  /**
440  A function called when the window gains or loses the keyboard focus.
441  The default implementation does nothing.
442  */
443  virtual void onFocus(bool focus, CrossingMode mode);
444 
445  /**
446  A function called when the window is resized.
447  If there is a top-level widget associated with this window, its size will be set right after this function.
448  The default implementation sets up drawing context where necessary.
449  */
450  virtual void onReshape(uint width, uint height);
451 
452  /**
453  A function called when scale factor requested for this window changes.
454  The default implementation does nothing.
455  WARNING function needs a proper name
456  */
457  virtual void onScaleFactorChanged(double scaleFactor);
458 
459 #ifndef DGL_FILE_BROWSER_DISABLED
460  /**
461  A function called when a path is selected by the user, as triggered by openFileBrowser().
462  This action happens after the user confirms the action, so the file browser dialog will be closed at this point.
463  The default implementation does nothing.
464  */
465  virtual void onFileSelected(const char* filename);
466 
467  /** DEPRECATED Use onFileSelected(). */
468  DISTRHO_DEPRECATED_BY("onFileSelected(const char*)")
469  inline virtual void fileBrowserSelected(const char* filename) { return onFileSelected(filename); }
470 #endif
471 
472 private:
473  PrivateData* const pData;
474  friend class Application;
475  friend class PluginWindow;
476  friend class TopLevelWidget;
477 
478  /** @internal */
479  explicit Window(Application& app,
480  uintptr_t parentWindowHandle,
481  uint width,
482  uint height,
483  double scaleFactor,
484  bool resizable,
485  bool doPostInit);
486 
487  DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Window);
488 };
489 
490 // -----------------------------------------------------------------------
491 
492 END_NAMESPACE_DGL
493 
494 #endif // DGL_WINDOW_HPP_INCLUDED
Window::runAsModal
void runAsModal(bool blockWait=false)
Window::isResizable
bool isResizable() const noexcept
Window::ScopedGraphicsContext::ScopedGraphicsContext
ScopedGraphicsContext(Window &window)
Window::FileBrowserOptions::title
const char * title
Definition: Window.hpp:75
Window::fileBrowserSelected
virtual void fileBrowserSelected(const char *filename)
Definition: Window.hpp:469
Window::setHeight
void setHeight(uint height)
Window::onFocus
virtual void onFocus(bool focus, CrossingMode mode)
Window::FileBrowserOptions::startDir
const char * startDir
Definition: Window.hpp:73
GraphicsContext
Definition: Base.hpp:154
Window::show
void show()
Window::FileBrowserOptions::Buttons
Definition: Window.hpp:81
Window::getNativeWindowHandle
uintptr_t getNativeWindowHandle() const noexcept
Window::isVisible
bool isVisible() const noexcept
Window::FileBrowserOptions::FileBrowserOptions
FileBrowserOptions()
Definition: Window.hpp:97
Window::setTitle
void setTitle(const char *title)
Window::setGeometryConstraints
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, bool keepAspectRatio=false, bool automaticallyScale=false)
Window
Definition: Window.hpp:50
Window::onScaleFactorChanged
virtual void onScaleFactorChanged(double scaleFactor)
Window::FileBrowserOptions::Buttons::listAllFiles
ButtonState listAllFiles
Definition: Window.hpp:83
Window::getTitle
const char * getTitle() const noexcept
Window::getScaleFactor
double getScaleFactor() const noexcept
Window::~Window
virtual ~Window()
Size
Definition: Geometry.hpp:132
Rectangle
Definition: Geometry.hpp:30
Application
Definition: Application.hpp:36
Window::repaint
void repaint() noexcept
Window::getWidth
uint getWidth() const noexcept
Window::onClose
virtual bool onClose()
Window::getGraphicsContext
const GraphicsContext & getGraphicsContext() const noexcept
Window::FileBrowserOptions::Buttons::showHidden
ButtonState showHidden
Definition: Window.hpp:85
Window::addIdleCallback
bool addIdleCallback(IdleCallback *callback, uint timerFrequencyInMs=0)
Window::onReshape
virtual void onReshape(uint width, uint height)
Window::setVisible
void setVisible(bool visible)
Window::renderToPicture
void renderToPicture(const char *filename)
Window::getIgnoringKeyRepeat
bool getIgnoringKeyRepeat() const noexcept
Definition: Window.hpp:415
Window::setWidth
void setWidth(uint width)
Window::setSize
void setSize(uint width, uint height)
Window::ScopedGraphicsContext::~ScopedGraphicsContext
~ScopedGraphicsContext()
Window::isEmbed
bool isEmbed() const noexcept
Window::openFileBrowser
bool openFileBrowser(const FileBrowserOptions &options=FileBrowserOptions())
Window::onFileSelected
virtual void onFileSelected(const char *filename)
Window::Window
Window(Application &app)
Window::FileBrowserOptions
Definition: Window.hpp:60
Window::getApp
Application & getApp() const noexcept
Window::exec
void exec(bool blockWait=false)
Definition: Window.hpp:423
Window::ScopedGraphicsContext::done
void done()
Window::FileBrowserOptions::ButtonState
ButtonState
Definition: Window.hpp:66
Window::removeIdleCallback
bool removeIdleCallback(IdleCallback *callback)
IdleCallback
Definition: Base.hpp:159
Window::FileBrowserOptions::Buttons::showPlaces
ButtonState showPlaces
Definition: Window.hpp:87
Window::getScaling
double getScaling() const noexcept
Definition: Window.hpp:419
Window::getSize
Size< uint > getSize() const noexcept
Window::focus
void focus()
Window::setResizable
void setResizable(bool resizable)
Window::setIgnoringKeyRepeat
void setIgnoringKeyRepeat(bool ignore) noexcept
Window::isIgnoringKeyRepeat
bool isIgnoringKeyRepeat() const noexcept
Window::getHeight
uint getHeight() const noexcept
Window::FileBrowserOptions::Buttons::Buttons
Buttons()
Definition: Window.hpp:90
TopLevelWidget
Definition: TopLevelWidget.hpp:46
Window::close
void close()
Window::ScopedGraphicsContext
Definition: Window.hpp:131
Window::getGeometryConstraints
Size< uint > getGeometryConstraints(bool &keepAspectRatio)
Window::hide
void hide()