1 /// Overlay I/O exists to provide nodes with the ability to add and control arbitrarily placed content, *laid over* 2 /// the remaining content. 3 /// 4 /// Typical examples of overlay content are context and dropdown menus, such that list navigation options and controls 5 /// separately from the rest, but also popup and modal windows, which appear over other content. Overlay components 6 /// are also used to guide drag-and-drop interactions. 7 /// 8 /// In Fluid, overlays may be limited to the window or canvas, as they are in [Raylib][fluid.raylib_view]. Other 9 /// setups may allow overlays to exceed window boundaries, which is common practice to give context menus more space. 10 /// 11 /// To use Overlay I/O, an `OverlayIO` instance must be active in the tree (activated through `Node.implementIO`) 12 /// to provide space for overlay nodes. A child node can then create overlays by loading the system with `Node.use` 13 /// or `Node.require` and calling `OverlayIO.addOverlay`. 14 /// 15 /// History: 16 /// * Introduced in Fluid 0.7.2 ([#319](https://git.samerion.com/Samerion/Fluid/issues/319)) 17 module fluid.io.overlay; 18 19 import fluid.types; 20 21 import fluid.future.context; 22 23 @safe: 24 25 /// This interface defines a way to create and operate overlay content. 26 /// 27 /// Instances of `OverlayIO` keep track of content as it is created and removed, and lay it out on the screen 28 /// using available methods. 29 interface OverlayIO : IO { 30 31 /// Defines known types of overlays. To use a type from this list, use `types`, spelled lowercase. 32 enum Types { 33 34 /// A dialog window, often serving to ask for confirmation or warning the user. 35 dialog, 36 37 /// A context menu, usually invoked by pressing the secondary mouse button, or the keyboard "menu" key. 38 context, 39 40 /// Dropdown menu, often used to select one option from a list of many, or opened from the app's 41 /// [menu bar](https://en.wikipedia.org/wiki/Menu_bar), if it has one. 42 dropdown, 43 44 /// A tooltip explaining the purpose or usage of a user interface component. 45 tooltip, 46 47 /// A drag-and-drop object. 48 draggable, 49 50 contextMenu = context, 51 52 } 53 54 /// Get an overlay type defined in `OverlayIO`. 55 /// 56 /// For example, to get an appropriate overlay type for a context menu, use `OverlayIO.types.context`. 57 /// 58 /// Returns: 59 /// A special struct that contains methods for creating instances of `OverlayType` 60 /// for types defined by `OverlayIO`. 61 static types() { 62 63 struct TypeDispatcher { 64 65 static OverlayType opDispatch(string name)() 66 if (__traits(hasMember, Types, name)) 67 do { 68 const type = __traits(getMember, Types, name); 69 return OverlayType(ioID!OverlayIO, type); 70 } 71 72 } 73 74 return TypeDispatcher(); 75 76 } 77 78 /// `types` is a shorthand that fills in the correct IO ID and type into `OverlayType`'s fields. 79 @("OverlayIO.types returns valid overlays") 80 unittest { 81 82 assert(OverlayIO.types.context == OverlayType(ioID!OverlayIO, OverlayIO.Types.context)); 83 assert(OverlayIO.types.tooltip == OverlayType(ioID!OverlayIO, OverlayIO.Types.tooltip)); 84 assert(OverlayIO.types.contextMenu == OverlayType(ioID!OverlayIO, OverlayIO.Types.contextMenu)); 85 86 } 87 88 /// Insert new overlay content. 89 /// 90 /// Overlay content should remain alive until the node specifies a `toRemove` status. 91 /// Overlays should not be added if they're not already in the node tree. 92 /// 93 /// See_Also: 94 /// `addChildOverlay` 95 /// Params: 96 /// node = Node containing the overlay content. 97 /// It must be a `Node` instance that implements the `Overlayable` interface. 98 /// Implementations should reject (through an `AssertError`) any objects that do not inherit from `Node`. 99 /// type = Type of the overlay node, as a fallback sequence. 100 /// This information that can be used to guide the overlay system by identifying the overlay's purpose. 101 /// See `OverlayIO.Types` and `OverlayIO.types` for commonly used types. 102 /// 103 /// The system should choose the first type from the list that it supports. 104 /// If none of the types in the list are recognized, `OverlayType.init` should be assumed. 105 /// All overlay systems must support `OverlayType.init` as a general purpose overlay. 106 void addOverlay(Overlayable node, OverlayType[] type...) nothrow; 107 108 /// Insert overlay content as a child of another. 109 /// 110 /// A child overlay is bound to its parent overlay, so if the parent is removed, the child should be removed too. 111 /// 112 /// See_Also: 113 /// `addOverlay` 114 /// Params: 115 /// parent = Parent node for the new overlay. 116 /// node = Child node; overlay to add. Same restrictions apply as for `addOverlay`. 117 /// type = Type of the overlay as a fallback sequence. 118 void addChildOverlay(Overlayable parent, Overlayable node, OverlayType[] type...) nothrow; 119 120 } 121 122 /// Defines a node that can be used as an overlay through `OverlayIO`. 123 /// 124 /// See_Also: 125 /// `fluid.io.overlay`, `OverlayIO`. 126 interface Overlayable { 127 128 /// An anchor the node is bound to, used for positioning the overlay. 129 /// 130 /// The overlay should be aligned to one of the rectangle's corners or edges, so that a point in the node's 131 /// rectangle should lie in the outline of the anchor. The node's `layout` property can be used for this: 132 /// 133 /// * For a `(start, start)` alignment, the point should be in the *top-left* corner of the overlay. 134 /// * For an `(end, end)` alignment, the point should lie in the *bottom-right* corner. 135 /// * An alignment value of `center` places the point in the *center* on the specified axis. 136 /// * The special value *fill* can be used to mean automatic alignment; use an alignment that is the best fit 137 /// for the available space. 138 /// 139 /// Even for overlays that are outside of the window's boundary, the rectangle should be in window space, 140 /// so that `(0,0)` is the window's top-left corner. 141 /// 142 /// Params: 143 /// space = Space available for the overlay, within which the anchor should be placed. 144 /// This may be the screen (relative to the window's position), the window itself, 145 /// or a fragment of the window. 146 /// Returns: 147 /// The anchor rectangle to use for positioning the node. 148 Rectangle getAnchor(Rectangle space) const nothrow; 149 150 /// Memory safe and `const` object comparison. 151 /// Returns: 152 /// True if this, and the other object, are the same object. 153 /// Params: 154 /// other = Object to compare to. 155 bool opEquals(const Object other) const; 156 157 } 158 159 /// Overlay type is used to specify the purpose of an overlay, and act as a guide for the overlay system's operations. 160 /// 161 /// Windowing systems commonly accept a window type specification which can aid in window management. 162 /// For example, it can be used by the system to style the overlay appropriately. 163 /// 164 /// Overlay types are extensible, and each I/O system can define their own set of types. Commonly used types such as 165 /// `dropdown` or `contextMenu` are defined as a part of `OverlayIO` and can be found in `OverlayIO.types`. 166 /// The `io` field specifies the I/O system, and the `number` field is used to distinguish between different types 167 /// that the system has defined. 168 /// 169 /// The special overlay type `OverlayType.init` with `io` set to `null` and number set to `0` is used to specify 170 /// a general purpose overlay type with no extra specification. 171 struct OverlayType { 172 173 /// ID of the I/O system defining the overlay type. 174 IOID ioID; 175 176 /// Number representing the type of the overlay. Meaning of this field is defined by the I/O system, but usually 177 /// it corresponds to an enum member, as it does for `OverlayIO`. 178 int number; 179 180 }