432 lines
14 KiB
C++
432 lines
14 KiB
C++
// Aseprite UI Library
|
|
// Copyright (C) 2018-2022 Igara Studio S.A.
|
|
// Copyright (C) 2001-2018 David Capello
|
|
//
|
|
// This file is released under the terms of the MIT license.
|
|
// Read LICENSE.txt for more information.
|
|
|
|
#ifndef UI_WIDGET_H_INCLUDED
|
|
#define UI_WIDGET_H_INCLUDED
|
|
#pragma once
|
|
|
|
#include "gfx/border.h"
|
|
#include "gfx/color.h"
|
|
#include "gfx/point.h"
|
|
#include "gfx/rect.h"
|
|
#include "gfx/region.h"
|
|
#include "gfx/size.h"
|
|
#include "obs/signal.h"
|
|
#include "os/font.h"
|
|
#include "ui/base.h"
|
|
#include "ui/component.h"
|
|
#include "ui/graphics.h"
|
|
#include "ui/widget_type.h"
|
|
#include "ui/widgets_list.h"
|
|
|
|
#include <string>
|
|
|
|
#define ASSERT_VALID_WIDGET(widget) ASSERT((widget) != nullptr)
|
|
|
|
namespace ui {
|
|
|
|
class InitThemeEvent;
|
|
class KeyMessage;
|
|
class LoadLayoutEvent;
|
|
class Manager;
|
|
class Message;
|
|
class MouseMessage;
|
|
class PaintEvent;
|
|
class ResizeEvent;
|
|
class SaveLayoutEvent;
|
|
class SizeHintEvent;
|
|
class Style;
|
|
class Theme;
|
|
class Window;
|
|
|
|
class Widget : public Component {
|
|
public:
|
|
|
|
// ===============================================================
|
|
// CTOR & DTOR
|
|
// ===============================================================
|
|
|
|
Widget(WidgetType type = kGenericWidget);
|
|
virtual ~Widget();
|
|
|
|
// Safe way to delete a widget when it is not in the manager message
|
|
// queue anymore.
|
|
void deferDelete();
|
|
|
|
// Main properties.
|
|
|
|
WidgetType type() const { return m_type; }
|
|
void setType(WidgetType type) { m_type = type; } // TODO remove this function
|
|
|
|
const std::string& id() const { return m_id; }
|
|
void setId(const char* id) { m_id = id; }
|
|
|
|
int flags() const { return m_flags; }
|
|
bool hasFlags(int flags) const { return ((m_flags & flags) == flags); }
|
|
void enableFlags(int flags) { m_flags |= flags; }
|
|
void disableFlags(int flags) { m_flags &= ~flags; }
|
|
|
|
int align() const { return (m_flags & ALIGN_MASK); }
|
|
void setAlign(int align) {
|
|
m_flags = ((m_flags & PROPERTIES_MASK) |
|
|
(align & ALIGN_MASK));
|
|
}
|
|
|
|
// Text property.
|
|
|
|
bool hasText() const { return hasFlags(HAS_TEXT); }
|
|
|
|
const std::string& text() const { return m_text; }
|
|
int textInt() const;
|
|
double textDouble() const;
|
|
void setText(const std::string& text);
|
|
void setTextf(const char* text, ...);
|
|
void setTextQuiet(const std::string& text);
|
|
|
|
int textWidth() const;
|
|
int textHeight() const;
|
|
|
|
gfx::Size textSize() const {
|
|
return gfx::Size(textWidth(), textHeight());
|
|
}
|
|
|
|
// ===============================================================
|
|
// COMMON PROPERTIES
|
|
// ===============================================================
|
|
|
|
// True if this widget and all its ancestors are visible.
|
|
bool isVisible() const;
|
|
void setVisible(bool state);
|
|
|
|
// True if this widget can receive user input (is not disabled).
|
|
bool isEnabled() const;
|
|
void setEnabled(bool state);
|
|
|
|
// True if this widget is selected (pushed in case of a button, or
|
|
// checked in the case of a check-box).
|
|
bool isSelected() const;
|
|
void setSelected(bool state);
|
|
|
|
// True if this widget wants more space when it's inside a Box
|
|
// parent.
|
|
bool isExpansive() const;
|
|
void setExpansive(bool state);
|
|
|
|
// True if this is a decorative widget created by the current
|
|
// theme. Decorative widgets are arranged by the theme instead that
|
|
// the parent's widget.
|
|
bool isDecorative() const;
|
|
void setDecorative(bool state);
|
|
|
|
// True if this widget can receive the keyboard focus.
|
|
bool isFocusStop() const;
|
|
void setFocusStop(bool state);
|
|
|
|
// True if this widget wants the focus by default when it's shown by
|
|
// first time (e.g. when its parent window is opened).
|
|
void setFocusMagnet(bool state);
|
|
bool isFocusMagnet() const;
|
|
|
|
// ===============================================================
|
|
// LOOK & FEEL
|
|
// ===============================================================
|
|
|
|
os::Font* font() const;
|
|
|
|
// Gets the background color of the widget.
|
|
gfx::Color bgColor() const {
|
|
if (gfx::geta(m_bgColor) == 0 && m_parent)
|
|
return m_parent->bgColor();
|
|
else
|
|
return m_bgColor;
|
|
}
|
|
|
|
// Sets the background color of the widget
|
|
void setBgColor(gfx::Color color);
|
|
|
|
Theme* theme() const { return m_theme; }
|
|
Style* style() const { return m_style; }
|
|
void setTheme(Theme* theme);
|
|
void setStyle(Style* style);
|
|
void initTheme();
|
|
|
|
// ===============================================================
|
|
// PARENTS & CHILDREN
|
|
// ===============================================================
|
|
|
|
Window* window() const;
|
|
Widget* parent() const { return m_parent; }
|
|
int parentIndex() const { return m_parentIndex; }
|
|
Manager* manager() const;
|
|
|
|
// Returns a list of children.
|
|
const WidgetsList& children() const { return m_children; }
|
|
bool hasChildren() const { return !m_children.empty(); }
|
|
|
|
Widget* at(int index) { return m_children[index]; }
|
|
int getChildIndex(Widget* child);
|
|
|
|
// Returns the first/last child or nullptr if it doesn't exist.
|
|
Widget* firstChild() {
|
|
return (hasChildren() ? m_children.front(): nullptr);
|
|
}
|
|
Widget* lastChild() {
|
|
return (hasChildren() ? m_children.back(): nullptr);
|
|
}
|
|
|
|
// Returns the next or previous siblings.
|
|
Widget* nextSibling();
|
|
Widget* previousSibling();
|
|
|
|
Widget* pick(const gfx::Point& pt,
|
|
const bool checkParentsVisibility = true) const;
|
|
bool hasChild(Widget* child);
|
|
bool hasAncestor(Widget* ancestor);
|
|
Widget* findChild(const char* id) const;
|
|
|
|
// Returns a widget in the same window that is located "sibling".
|
|
Widget* findSibling(const char* id) const;
|
|
|
|
// Finds a child with the specified ID and dynamic-casts it to type
|
|
// T.
|
|
template<class T>
|
|
T* findChildT(const char* id) const {
|
|
return dynamic_cast<T*>(findChild(id));
|
|
}
|
|
|
|
template<class T>
|
|
T* findFirstChildByType() const {
|
|
for (auto child : m_children) {
|
|
if (T* specificChild = dynamic_cast<T*>(child))
|
|
return specificChild;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void addChild(Widget* child);
|
|
void removeChild(Widget* child);
|
|
void removeAllChildren();
|
|
void replaceChild(Widget* oldChild, Widget* newChild);
|
|
void insertChild(int index, Widget* child);
|
|
void moveChildTo(Widget* thisChild, Widget* toThisPosition);
|
|
|
|
// ===============================================================
|
|
// LAYOUT & CONSTRAINT
|
|
// ===============================================================
|
|
|
|
void layout();
|
|
void loadLayout();
|
|
void saveLayout();
|
|
|
|
void setDecorativeWidgetBounds();
|
|
|
|
// ===============================================================
|
|
// POSITION & GEOMETRY
|
|
// ===============================================================
|
|
|
|
gfx::Rect bounds() const { return m_bounds; }
|
|
gfx::Point origin() const { return m_bounds.origin(); }
|
|
gfx::Size size() const { return m_bounds.size(); }
|
|
|
|
gfx::Rect clientBounds() const {
|
|
return gfx::Rect(0, 0, m_bounds.w, m_bounds.h);
|
|
}
|
|
|
|
gfx::Rect childrenBounds() const;
|
|
gfx::Rect clientChildrenBounds() const;
|
|
|
|
// Sets the bounds of the widget generating a onResize() event.
|
|
void setBounds(const gfx::Rect& rc);
|
|
|
|
// Sets the bounds of the widget without generating any kind of
|
|
// event. This member function must be used if you override
|
|
// onResize() and want to change the size of the widget without
|
|
// generating recursive onResize() events.
|
|
void setBoundsQuietly(const gfx::Rect& rc);
|
|
void offsetWidgets(int dx, int dy);
|
|
|
|
const gfx::Size& minSize() const { return m_minSize; }
|
|
const gfx::Size& maxSize() const { return m_maxSize; }
|
|
void setMinSize(const gfx::Size& sz);
|
|
void setMaxSize(const gfx::Size& sz);
|
|
void resetMinSize();
|
|
void resetMaxSize();
|
|
|
|
const gfx::Border& border() const { return m_border; }
|
|
void setBorder(const gfx::Border& border);
|
|
|
|
int childSpacing() const { return m_childSpacing; }
|
|
void setChildSpacing(int childSpacing);
|
|
|
|
void noBorderNoChildSpacing();
|
|
|
|
// Flags for getDrawableRegion()
|
|
enum DrawableRegionFlags {
|
|
kCutTopWindows = 1, // Cut areas where are windows on top.
|
|
kUseChildArea = 2, // Use areas where are children.
|
|
kCutTopWindowsAndUseChildArea = kCutTopWindows | kUseChildArea,
|
|
};
|
|
|
|
void getRegion(gfx::Region& region);
|
|
void getDrawableRegion(gfx::Region& region, DrawableRegionFlags flags);
|
|
|
|
gfx::Point toClient(const gfx::Point& pt) const {
|
|
return pt - m_bounds.origin();
|
|
}
|
|
gfx::Rect toClient(const gfx::Rect& rc) const {
|
|
return gfx::Rect(rc).offset(-m_bounds.x, -m_bounds.y);
|
|
}
|
|
|
|
void getTextIconInfo(
|
|
gfx::Rect* box,
|
|
gfx::Rect* text = nullptr,
|
|
gfx::Rect* icon = nullptr,
|
|
int icon_align = 0, int icon_w = 0, int icon_h = 0);
|
|
|
|
// ===============================================================
|
|
// REFRESH ISSUES
|
|
// ===============================================================
|
|
|
|
bool isDoubleBuffered() const;
|
|
void setDoubleBuffered(bool doubleBuffered);
|
|
|
|
bool isTransparent() const;
|
|
void setTransparent(bool transparent);
|
|
|
|
void invalidate();
|
|
void invalidateRect(const gfx::Rect& rect);
|
|
void invalidateRegion(const gfx::Region& region);
|
|
|
|
// Returns the region to generate PaintMessages. It's cleared
|
|
// after flushRedraw() is called.
|
|
const gfx::Region& getUpdateRegion() const {
|
|
return m_updateRegion;
|
|
}
|
|
|
|
// Generates paint messages for the current update region.
|
|
void flushRedraw();
|
|
|
|
GraphicsPtr getGraphics(const gfx::Rect& clip);
|
|
|
|
// ===============================================================
|
|
// GUI MANAGER
|
|
// ===============================================================
|
|
|
|
bool sendMessage(Message* msg);
|
|
void closeWindow();
|
|
|
|
void broadcastMouseMessage(WidgetsList& targets);
|
|
|
|
// ===============================================================
|
|
// SIZE & POSITION
|
|
// ===============================================================
|
|
|
|
gfx::Size sizeHint();
|
|
gfx::Size sizeHint(const gfx::Size& fitIn);
|
|
void setSizeHint(const gfx::Size& fixedSize);
|
|
void setSizeHint(int fixedWidth, int fixedHeight);
|
|
void resetSizeHint();
|
|
|
|
// ===============================================================
|
|
// MOUSE, FOCUS & KEYBOARD
|
|
// ===============================================================
|
|
|
|
void requestFocus();
|
|
void releaseFocus();
|
|
void captureMouse();
|
|
void releaseMouse();
|
|
|
|
bool hasFocus() const { return hasFlags(HAS_FOCUS); }
|
|
bool hasMouse() const { return hasFlags(HAS_MOUSE); }
|
|
bool hasCapture() const { return hasFlags(HAS_CAPTURE); }
|
|
bool hasMouseOver() const;
|
|
|
|
// Offer the capture to widgets of the given type. Returns true if
|
|
// the capture was passed to other widget.
|
|
bool offerCapture(ui::MouseMessage* mouseMsg, int widget_type);
|
|
|
|
// Returns lower-case letter that represet the mnemonic of the widget
|
|
// (the underscored character, i.e. the letter after & symbol).
|
|
int mnemonic() const { return m_mnemonic; }
|
|
void setMnemonic(int mnemonic);
|
|
|
|
// Assigns mnemonic from the character preceded by the given
|
|
// escapeChar ('&' by default).
|
|
void processMnemonicFromText(int escapeChar = '&');
|
|
|
|
// Returns true if the mnemonic character is pressed.
|
|
bool isMnemonicPressed(const ui::KeyMessage* keyMsg) const;
|
|
|
|
// Signals
|
|
obs::signal<void()> InitTheme;
|
|
|
|
protected:
|
|
// ===============================================================
|
|
// MESSAGE PROCESSING
|
|
// ===============================================================
|
|
|
|
virtual bool onProcessMessage(Message* msg);
|
|
|
|
// ===============================================================
|
|
// EVENTS
|
|
// ===============================================================
|
|
|
|
virtual void onInvalidateRegion(const gfx::Region& region);
|
|
virtual void onSizeHint(SizeHintEvent& ev);
|
|
virtual void onLoadLayout(LoadLayoutEvent& ev);
|
|
virtual void onSaveLayout(SaveLayoutEvent& ev);
|
|
virtual void onResize(ResizeEvent& ev);
|
|
virtual void onPaint(PaintEvent& ev);
|
|
virtual void onBroadcastMouseMessage(WidgetsList& targets);
|
|
virtual void onInitTheme(InitThemeEvent& ev);
|
|
virtual void onSetDecorativeWidgetBounds();
|
|
virtual void onVisible(bool visible);
|
|
virtual void onEnable(bool enabled);
|
|
virtual void onSelect(bool selected);
|
|
virtual void onSetText();
|
|
virtual void onSetBgColor();
|
|
virtual int onGetTextInt() const;
|
|
virtual double onGetTextDouble() const;
|
|
|
|
private:
|
|
void removeChild(const WidgetsList::iterator& it);
|
|
void paint(Graphics* graphics,
|
|
const gfx::Region& drawRegion,
|
|
const bool isBg);
|
|
bool paintEvent(Graphics* graphics,
|
|
const bool isBg);
|
|
void setDirtyFlag();
|
|
|
|
WidgetType m_type; // Widget's type
|
|
std::string m_id; // Widget's id
|
|
int m_flags; // Special boolean properties (see flags in ui/base.h)
|
|
Theme* m_theme; // Widget's theme
|
|
Style* m_style;
|
|
std::string m_text; // Widget text
|
|
mutable os::FontRef m_font; // Cached font returned by the theme
|
|
gfx::Color m_bgColor; // Background color
|
|
gfx::Rect m_bounds;
|
|
gfx::Region m_updateRegion; // Region to be redrawed.
|
|
WidgetsList m_children; // Sub-widgets
|
|
Widget* m_parent; // Who is the parent?
|
|
int m_parentIndex; // Location/index of this widget in the parent's Widget::m_children vector
|
|
gfx::Size* m_sizeHint;
|
|
int m_mnemonic; // Keyboard shortcut to access this widget like Alt+mnemonic
|
|
|
|
// Widget size limits
|
|
gfx::Size m_minSize, m_maxSize;
|
|
|
|
gfx::Border m_border; // Border separation with the parent
|
|
int m_childSpacing; // Separation between children
|
|
};
|
|
|
|
WidgetType register_widget_type();
|
|
|
|
} // namespace ui
|
|
|
|
#endif
|