| 1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
| 2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
| 3 | #pragma once |
| 4 | |
| 5 | #include "BsPrerequisites.h" |
| 6 | #include "GUI/BsGUIElementBase.h" |
| 7 | #include "GUI/BsGUIOptions.h" |
| 8 | #include "Math/BsRect2I.h" |
| 9 | #include "Math/BsVector2I.h" |
| 10 | #include "Image/BsColor.h" |
| 11 | |
| 12 | namespace bs |
| 13 | { |
| 14 | class GUINavGroup; |
| 15 | |
| 16 | /** @addtogroup Implementation |
| 17 | * @{ |
| 18 | */ |
| 19 | |
| 20 | enum class GUIElementOption |
| 21 | { |
| 22 | /** |
| 23 | * Enable this option if you want pointer events to pass through this element by default. This will allow elements |
| 24 | * underneath this element to receive pointer events. |
| 25 | */ |
| 26 | ClickThrough = 0x01, |
| 27 | |
| 28 | /** |
| 29 | * Enable this option if the element accepts keyboard/gamepad input focus. This will allow the element to be |
| 30 | * navigated to using keys/buttons. |
| 31 | */ |
| 32 | AcceptsKeyFocus = 0x02 |
| 33 | }; |
| 34 | |
| 35 | typedef Flags<GUIElementOption> GUIElementOptions; |
| 36 | BS_FLAGS_OPERATORS(GUIElementOption) |
| 37 | |
| 38 | /** |
| 39 | * Represents parent class for all visible GUI elements. Contains methods needed for positioning, rendering and |
| 40 | * handling input. |
| 41 | */ |
| 42 | class BS_EXPORT GUIElement : public GUIElementBase |
| 43 | { |
| 44 | public: |
| 45 | /** Different sub-types of GUI elements. */ |
| 46 | enum class ElementType |
| 47 | { |
| 48 | Label, |
| 49 | Button, |
| 50 | Toggle, |
| 51 | Texture, |
| 52 | InputBox, |
| 53 | ListBox, |
| 54 | ScrollArea, |
| 55 | Layout, |
| 56 | Undefined |
| 57 | }; |
| 58 | |
| 59 | public: |
| 60 | GUIElement(String styleName, const GUIDimensions& dimensions, GUIElementOptions options = GUIElementOptions(0)); |
| 61 | virtual ~GUIElement() = default; |
| 62 | |
| 63 | /** |
| 64 | * Change the GUI element focus state. |
| 65 | * |
| 66 | * @param[in] enabled Give the element focus or take it away. |
| 67 | * @param[in] clear If true the focus will be cleared from any elements currently in focus. Otherwise |
| 68 | * the element will just be appended to the in-focus list (if enabling focus). |
| 69 | */ |
| 70 | virtual void setFocus(bool enabled, bool clear = false); |
| 71 | |
| 72 | /** Sets the tint of the GUI element. */ |
| 73 | virtual void setTint(const Color& color); |
| 74 | |
| 75 | /** @copydoc GUIElementBase::resetDimensions */ |
| 76 | void resetDimensions() override; |
| 77 | |
| 78 | /** Sets new style to be used by the element. */ |
| 79 | void setStyle(const String& styleName); |
| 80 | |
| 81 | /** Returns the name of the style used by this element. */ |
| 82 | const String& getStyleName() const { return mStyleName; } |
| 83 | |
| 84 | /** A set of flags controlling various aspects of the GUIElement. See GUIElementOptions. */ |
| 85 | void setOptionFlags(GUIElementOptions options) { mOptionFlags = options; } |
| 86 | |
| 87 | /** @copydoc setOptionFlags */ |
| 88 | GUIElementOptions getOptionFlags() const { return mOptionFlags; } |
| 89 | |
| 90 | /** |
| 91 | * Assigns a new context menu that will be opened when the element is right clicked. Null is allowed in case no |
| 92 | * context menu is wanted. |
| 93 | */ |
| 94 | void (const SPtr<GUIContextMenu>& ) { mContextMenu = menu; } |
| 95 | |
| 96 | /** |
| 97 | * Sets a navigation group that determines in what order are GUI elements visited when using a keyboard or gamepad |
| 98 | * to switch between the elements. If you don't set a navigation group the elements will inherit the default |
| 99 | * navigation group from their parent GUIWidget. Also see setNavGroupIndex(). |
| 100 | */ |
| 101 | void setNavGroup(const SPtr<GUINavGroup>& navGroup); |
| 102 | |
| 103 | /** |
| 104 | * Sets the index that determines in what order is the element visited compared to all the other elements in the |
| 105 | * nav-group. Elements with lower index will be visited before elements with a higher index. Elements with index |
| 106 | * 0 (the default) are special and will have their visit order determines by their position compared to other |
| 107 | * elements. The applied index is tied to the nav-group, so if the nav-group changes the index will need to be |
| 108 | * re-applied. |
| 109 | */ |
| 110 | void setNavGroupIndex(INT32 index); |
| 111 | |
| 112 | /** @copydoc GUIElementBase::getVisibleBounds */ |
| 113 | Rect2I getVisibleBounds() override; |
| 114 | |
| 115 | /** |
| 116 | * Destroy the element. Removes it from parent and widget, and queues it for deletion. Element memory will be |
| 117 | * released delayed, next frame. |
| 118 | */ |
| 119 | static void destroy(GUIElement* element); |
| 120 | |
| 121 | /** Triggered when the element loses or gains focus. */ |
| 122 | Event<void(bool)> onFocusChanged; |
| 123 | |
| 124 | public: // ***** INTERNAL ****** |
| 125 | /** @name Internal |
| 126 | * @{ |
| 127 | */ |
| 128 | |
| 129 | /** |
| 130 | * Returns the number of separate render elements in the GUI element. |
| 131 | * |
| 132 | * @return The number render elements. |
| 133 | * |
| 134 | * @note |
| 135 | * GUI system attempts to reduce the number of GUI meshes so it will group sprites based on their material and |
| 136 | * textures. One render elements represents a group of such sprites that share a material/texture. |
| 137 | */ |
| 138 | virtual UINT32 _getNumRenderElements() const = 0; |
| 139 | |
| 140 | /** |
| 141 | * Gets a material for the specified render element index. |
| 142 | * |
| 143 | * @return Handle to the material. |
| 144 | * |
| 145 | * @see _getNumRenderElements() |
| 146 | */ |
| 147 | virtual const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const = 0; |
| 148 | |
| 149 | /** |
| 150 | * Returns the type of mesh and number of vertices and indices that the specified render element will use. You will |
| 151 | * need this value when creating the buffers before calling _fillBuffer(). |
| 152 | * |
| 153 | * @see _getNumRenderElements() |
| 154 | * @see _fillBuffer() |
| 155 | */ |
| 156 | virtual void _getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const = 0; |
| 157 | |
| 158 | /** |
| 159 | * Fill the pre-allocated vertex, uv and index buffers with the mesh data for the specified render element. |
| 160 | * |
| 161 | * @param[out] vertices Previously allocated buffer where to store the vertices. Output is expected |
| 162 | * to match the GUIMeshType as returned by _getMeshInfo. |
| 163 | * @param[out] indices Previously allocated buffer where to store the indices. |
| 164 | * @param[in] vertexOffset At which vertex should the method start filling the buffer. |
| 165 | * @param[in] indexOffset At which index should the method start filling the buffer. |
| 166 | * @param[in] maxNumVerts Total number of vertices the buffers were allocated for. Used only for memory |
| 167 | * safety. |
| 168 | * @param[in] maxNumIndices Total number of indices the buffers were allocated for. Used only for memory |
| 169 | * safety. |
| 170 | * @param[in] renderElementIdx Zero-based index of the render element. |
| 171 | * |
| 172 | * @see _getNumRenderElements() |
| 173 | * @see _getMeshInfo() |
| 174 | */ |
| 175 | virtual void _fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset, |
| 176 | UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const = 0; |
| 177 | |
| 178 | /** |
| 179 | * Recreates the internal render elements. Must be called before fillBuffer if element is dirty. Marks the element |
| 180 | * as non dirty. |
| 181 | */ |
| 182 | void _updateRenderElements(); |
| 183 | |
| 184 | /** Gets internal element style representing the exact type of GUI element in this object. */ |
| 185 | virtual ElementType _getElementType() const { return ElementType::Undefined; } |
| 186 | |
| 187 | /** |
| 188 | * Called when a mouse event is received on any GUI element the mouse is interacting with. Return true if you have |
| 189 | * processed the event and don't want other elements to process it. |
| 190 | */ |
| 191 | virtual bool _mouseEvent(const GUIMouseEvent& ev); |
| 192 | |
| 193 | /** |
| 194 | * Called when some text is input and the GUI element has input focus. Return true if you have processed the event |
| 195 | * and don't want other elements to process it. |
| 196 | */ |
| 197 | virtual bool _textInputEvent(const GUITextInputEvent& ev); |
| 198 | |
| 199 | /** |
| 200 | * Called when a command event is triggered. Return true if you have processed the event and don't want other |
| 201 | * elements to process it. |
| 202 | */ |
| 203 | virtual bool _commandEvent(const GUICommandEvent& ev); |
| 204 | |
| 205 | /** |
| 206 | * Called when a virtual button is pressed/released and the GUI element has input focus. Return true if you have |
| 207 | * processed the event and don't want other elements to process it. |
| 208 | */ |
| 209 | virtual bool _virtualButtonEvent(const GUIVirtualButtonEvent& ev); |
| 210 | |
| 211 | /** Set element part of element depth. Less significant than both widget and area depth. */ |
| 212 | void _setElementDepth(UINT8 depth); |
| 213 | |
| 214 | /** Retrieve element part of element depth. Less significant than both widget and area depth. */ |
| 215 | UINT8 _getElementDepth() const; |
| 216 | |
| 217 | /** @copydoc GUIElementBase::_setLayoutData */ |
| 218 | void _setLayoutData(const GUILayoutData& data) override; |
| 219 | |
| 220 | /** @copydoc GUIElementBase::_changeParentWidget */ |
| 221 | void _changeParentWidget(GUIWidget* widget) override; |
| 222 | |
| 223 | /** |
| 224 | * Returns depth for a specific render element. This contains a combination of widget depth (8 bit(, area depth |
| 225 | * (16 bit) and render element depth (8 bit). |
| 226 | * |
| 227 | * @see _getNumRenderElements |
| 228 | */ |
| 229 | virtual UINT32 _getRenderElementDepth(UINT32 renderElementIdx) const { return _getDepth(); } |
| 230 | |
| 231 | /** |
| 232 | * Returns the range of depths that the child elements can be rendered it. |
| 233 | * |
| 234 | * @note |
| 235 | * For example if you are rendering a button with an image and a text you will want the text to be rendered in front |
| 236 | * of the image at a different depth, which means the depth range is 2 (0 for text, 1 for background image). |
| 237 | */ |
| 238 | virtual UINT32 _getRenderElementDepthRange() const { return 1; } |
| 239 | |
| 240 | /** Gets internal element style representing the exact type of GUI element in this object. */ |
| 241 | Type _getType() const override { return GUIElementBase::Type::Element; } |
| 242 | |
| 243 | /** Checks if element has been destroyed and is queued for deletion. */ |
| 244 | bool _isDestroyed() const override { return mIsDestroyed; } |
| 245 | |
| 246 | /** Update element style based on active GUI skin and style name. */ |
| 247 | void _refreshStyle(); |
| 248 | |
| 249 | /** Gets the currently active element style. */ |
| 250 | const GUIElementStyle* _getStyle() const { return mStyle; } |
| 251 | |
| 252 | /** Gets GUI element bounds relative to parent widget, clipped by specified clip rect. */ |
| 253 | const Rect2I& _getClippedBounds() const { return mClippedBounds; } |
| 254 | |
| 255 | /** |
| 256 | * Returns GUI element padding. Padding is modified by changing element style and determines minimum distance |
| 257 | * between different GUI elements. |
| 258 | */ |
| 259 | const RectOffset& _getPadding() const override; |
| 260 | |
| 261 | /** |
| 262 | * Returns GUI element depth. This includes widget and area depth, but does not include specific per-render-element |
| 263 | * depth. |
| 264 | */ |
| 265 | UINT32 _getDepth() const { return mLayoutData.depth; } |
| 266 | |
| 267 | /** Returns the navigation group this element belongs to. See setNavGroup(). */ |
| 268 | SPtr<GUINavGroup> _getNavGroup() const; |
| 269 | |
| 270 | /** Checks is the specified position within GUI element bounds. Position is relative to parent GUI widget. */ |
| 271 | virtual bool _isInBounds(const Vector2I position) const; |
| 272 | |
| 273 | /** Checks if the GUI element has a custom cursor and outputs the cursor type if it does. */ |
| 274 | virtual bool _hasCustomCursor(const Vector2I position, CursorType& type) const { return false; } |
| 275 | |
| 276 | /** Checks if the GUI element accepts a drag and drop operation of the specified type. */ |
| 277 | virtual bool _acceptDragAndDrop(const Vector2I position, UINT32 typeId) const { return false; } |
| 278 | |
| 279 | /** Returns a context menu if a GUI element has one. Otherwise returns nullptr. */ |
| 280 | virtual SPtr<GUIContextMenu> () const; |
| 281 | |
| 282 | /** Returns text to display when hovering over the element. Returns empty string if no tooltip. */ |
| 283 | virtual String _getTooltip() const { return StringUtil::BLANK; } |
| 284 | |
| 285 | /** Returns a clip rectangle relative to the element, used for offsetting the input text. */ |
| 286 | virtual Vector2I _getTextInputOffset() const { return Vector2I(); } |
| 287 | |
| 288 | /** Returns a clip rectangle relative to the element, used for clipping the input text. */ |
| 289 | virtual Rect2I _getTextInputRect() const { return Rect2I(); } |
| 290 | |
| 291 | /** @} */ |
| 292 | |
| 293 | protected: |
| 294 | /** Called whenever render elements are dirty and need to be rebuilt. */ |
| 295 | virtual void updateRenderElementsInternal(); |
| 296 | |
| 297 | /** |
| 298 | * Called whenever element clipped bounds need to be recalculated. (for example when width, height or clip |
| 299 | * rectangles changes). |
| 300 | */ |
| 301 | virtual void updateClippedBounds(); |
| 302 | |
| 303 | /** |
| 304 | * Helper method that returns style name used by an element of a certain type. If override style is empty, default |
| 305 | * style for that type is returned. |
| 306 | */ |
| 307 | template<class T> |
| 308 | static const String& getStyleName(const String& overrideStyle) |
| 309 | { |
| 310 | if(overrideStyle == StringUtil::BLANK) |
| 311 | return T::getGUITypeName(); |
| 312 | |
| 313 | return overrideStyle; |
| 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Attempts to find a sub-style for the specified type in the currently set GUI element style. If one cannot be |
| 318 | * found empty string is returned. |
| 319 | */ |
| 320 | const String& getSubStyleName(const String& subStyleTypeName) const; |
| 321 | |
| 322 | /** Method that gets triggered whenever element style changes. */ |
| 323 | virtual void styleUpdated() { } |
| 324 | |
| 325 | /** Returns clipped bounds excluding the margins. Relative to parent widget. */ |
| 326 | Rect2I getCachedVisibleBounds() const; |
| 327 | |
| 328 | /** Returns bounds of the content contained within the GUI element. Relative to parent widget. */ |
| 329 | Rect2I getCachedContentBounds() const; |
| 330 | |
| 331 | /** |
| 332 | * Returns a clip rectangle that can be used for clipping the contents of this GUI element. Clip rect is relative |
| 333 | * to GUI element origin. |
| 334 | */ |
| 335 | Rect2I getCachedContentClipRect() const; |
| 336 | |
| 337 | /** Returns the tint that is applied to the GUI element. */ |
| 338 | Color getTint() const; |
| 339 | |
| 340 | bool mIsDestroyed = false; |
| 341 | GUIElementOptions mOptionFlags; |
| 342 | Rect2I mClippedBounds; |
| 343 | |
| 344 | private: |
| 345 | static const Color DISABLED_COLOR; |
| 346 | |
| 347 | const GUIElementStyle* mStyle; |
| 348 | String mStyleName; |
| 349 | |
| 350 | SPtr<GUIContextMenu> ; |
| 351 | SPtr<GUINavGroup> mNavGroup; |
| 352 | Color mColor; |
| 353 | }; |
| 354 | |
| 355 | /** @} */ |
| 356 | } |