[fcce3f1] | 1 | ========== |
---|
| 2 | owl_window |
---|
| 3 | ========== |
---|
| 4 | |
---|
| 5 | owl_window is intended to be a wrapper over ncurses WINDOW and PANEL |
---|
| 6 | objects to abstract away much of the nastiness that is ncurses. It |
---|
| 7 | provides redraw scheduling, and sane window moving and resizing. |
---|
| 8 | |
---|
| 9 | ------------------- |
---|
| 10 | Hierarchy and types |
---|
| 11 | ------------------- |
---|
| 12 | |
---|
| 13 | owl_window creates a hierarchy of window objects. The hierarchy |
---|
| 14 | currently uses ncurses' built-in window hierarchy system with |
---|
| 15 | subwins. This was easier to implement but has some caveats, detailed |
---|
| 16 | later. |
---|
| 17 | |
---|
| 18 | At the top level, we have the screen window which represents the |
---|
| 19 | actual screen. The screen window is not usually interacted with |
---|
| 20 | directly, but rather it is a handle to interface with the terminal as |
---|
| 21 | if it were a normal window. |
---|
| 22 | |
---|
| 23 | Underneath the screen, we have top-level windows, or panels. Panels |
---|
| 24 | are normal WINDOWs (newwin) connected to a PANEL (from |
---|
| 25 | libpanel). Panels may overlap freely and maintain a stacking |
---|
| 26 | order. (For now, this stacking order is not exposed except for a |
---|
| 27 | mechanism to move a panel to the top. This is not difficult to |
---|
| 28 | otherwise correct otherwise.) |
---|
| 29 | |
---|
| 30 | Under each panel is a tree of subwins. These are backed by ncurses |
---|
| 31 | subwins. Because ncurses subwins simply share their parent windows' |
---|
| 32 | buffer, we cannot provide as many nice guarantees about |
---|
| 33 | ordering. Sibling subwins may not overlap, and parents must |
---|
| 34 | (sometimes) take children position into account. More on this |
---|
| 35 | later. This model is sufficient for BarnOwl's current purposes. Should |
---|
| 36 | we need to, this may later be reworked. (Specifically, we'd want to |
---|
| 37 | back everything by pads and build a compositing window manager.) |
---|
| 38 | |
---|
| 39 | Each of these three types is temporarily stuffed into one type. We can |
---|
| 40 | later do subtyping of sorts, but that will require more heavy use of |
---|
| 41 | GObject. |
---|
| 42 | |
---|
| 43 | As an example, here is the graph used by BarnOwl's current interface: |
---|
| 44 | |
---|
| 45 | +==========+ |
---|
| 46 | || screen || |
---|
| 47 | +==========+ |
---|
| 48 | / \ |
---|
| 49 | +-----------+ +--------+ |
---|
| 50 | | mainpanel | | popwin | |
---|
| 51 | +-----------+ +--------+ |
---|
| 52 | / / | \ \ |
---|
| 53 | recwin sepwin msgwin typwin viewwin |
---|
| 54 | |
---|
| 55 | |
---|
| 56 | A window may be unlinked from its parent with a call to |
---|
| 57 | owl_window_unlink. From then on, the object is still accessible, but |
---|
| 58 | it will not do anything. If desired, we may add the ability to relink |
---|
| 59 | a window in future. This behavior allows us to safely hold references |
---|
| 60 | to windows, even after they have been "destroyed". |
---|
| 61 | |
---|
| 62 | ---------- |
---|
| 63 | Visibility |
---|
| 64 | ---------- |
---|
| 65 | |
---|
| 66 | Each window maintains state for whether or not the user has requested |
---|
| 67 | it be visible. A user calls owl_window_show to flip the 'shown' bit on |
---|
| 68 | a window and owl_window_show_all to do so recursively. Likewise, |
---|
| 69 | owl_window_hide disables this bit. If a window and all its parents are |
---|
| 70 | shown, then the user has requested this window be visible. |
---|
| 71 | |
---|
| 72 | We say a window is realized if it has a corresponding on-screen |
---|
| 73 | window. A window will only be realized if it is requested to be |
---|
| 74 | shown. Furthermore, the window's parent must also be realized (unless |
---|
| 75 | it is the screen). If a window is failed to be created for any reason |
---|
| 76 | (most notably if its dimensions are zero), owl_window will cope and |
---|
| 77 | consider it unrealized. |
---|
| 78 | |
---|
| 79 | This realized/unrealized state fixes two nuisances with the old code: |
---|
| 80 | |
---|
| 81 | First, owl_window can safely manage all NULL windows. Interacting |
---|
| 82 | code needn't check to avoid segfaults; the owl_window is never |
---|
| 83 | NULL, and code requesting the WINDOW will never be called when |
---|
| 84 | NULL. |
---|
| 85 | |
---|
| 86 | Second, we have a consistent handle to a logical window, despite |
---|
| 87 | changes in the physical window. This is important for resizing |
---|
| 88 | windows. It is difficult to safely resize windows in ncurses. It |
---|
| 89 | is usually far easier to destroy everything and recreate it. |
---|
| 90 | |
---|
| 91 | Note that this means owl_window will intentionally never expose the |
---|
| 92 | underlying WINDOW except during appropriate events. |
---|
| 93 | |
---|
| 94 | -------- |
---|
| 95 | Resizing |
---|
| 96 | -------- |
---|
| 97 | |
---|
| 98 | Windows may be moved and resized with owl_window_move, |
---|
| 99 | owl_window_resize, and owl_window_set_position. Internally, resizing |
---|
| 100 | is very simple. We unrealize the window, set new position, and then |
---|
| 101 | realize it at the new location. When a window changes size, it emits a |
---|
| 102 | "resized" signal which windows may react to. It may actually possible |
---|
| 103 | to optimize moves, but this has a slight nuisance with incorrect |
---|
| 104 | begy/begx values which cursors currently rely on. |
---|
| 105 | |
---|
| 106 | It is intended that top-level windows connect to this signal to change |
---|
| 107 | themselves, while windows containing subwins connect to their own |
---|
| 108 | signals to relayout their subwins. This is because top-level windows |
---|
| 109 | may be sized independently, while sibling subwins should not |
---|
| 110 | overlap. The signals are implemented currently with GObject signals. |
---|
| 111 | |
---|
| 112 | --------- |
---|
| 113 | Redrawing |
---|
| 114 | --------- |
---|
| 115 | |
---|
| 116 | Currently, users of widgets in BarnOwl must remember to call |
---|
| 117 | owl_foo_redisplay when a widget needs to be redrawn. This is quite |
---|
| 118 | annoying and an implementation detail that functions like |
---|
| 119 | owl_editwin_insert_string should take care of. To allow widget |
---|
| 120 | implementations to redraw themselves without calling an expensive |
---|
| 121 | redisplay many times, owl_window_dirty flags a window as "dirty". The |
---|
| 122 | framework will promise to redraw that window before the next doupdate, |
---|
| 123 | while repeated owl_window_dirty calls remain cheap. |
---|
| 124 | |
---|
| 125 | Windows currently redraw with a "redraw" GObject signal. This and the |
---|
| 126 | resize signal, is somewhat awkward for the editwin which attaches to |
---|
| 127 | and releases windows, as we must remember signal ids. However, if we |
---|
| 128 | make widget objects (owl_editwin, owl_mainwin, etc.) also GObjects, |
---|
| 129 | these signals will automatically detach. We may also consider a |
---|
| 130 | different mechanism than continuously attaching/detaching |
---|
| 131 | things. That's kinda weird anyway. |
---|
| 132 | |
---|
| 133 | (We may want to replace it with a normal callback at some |
---|
| 134 | point. Mostly using the GObject signals to play with them, and because |
---|
| 135 | we'd likely get some form of automatic binding generation.) |
---|