diff options
author | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-10 22:30:12 +0100 |
---|---|---|
committer | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-10 22:30:12 +0100 |
commit | 429d5f88b94ff28416cbfc6420b6389fa284df97 (patch) | |
tree | fb6fdaf7731de1ef396f98b748c56f3149801c84 /dw/widget.hh |
Import RTFL 0.1.1v0.1.1
Diffstat (limited to 'dw/widget.hh')
-rw-r--r-- | dw/widget.hh | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/dw/widget.hh b/dw/widget.hh new file mode 100644 index 0000000..d107fe1 --- /dev/null +++ b/dw/widget.hh @@ -0,0 +1,514 @@ +#ifndef __DW_WIDGET_HH__ +#define __DW_WIDGET_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +#include "../lout/identity.hh" + +/** + * \brief The type for callback functions. + */ +typedef void (*DW_Callback_t)(void *data); + +namespace dw { +namespace core { + +/** + * \brief The base class of all dillo widgets. + * + * \sa\ref dw-overview, \ref dw-layout-widgets + */ +class Widget: public lout::identity::IdentifiableObject +{ + friend class Layout; + +protected: + enum Flags { + /** + * \todo Comment this. + */ + RESIZE_QUEUED = 1 << 0, + + /** + * \todo Comment this. + */ + EXTREMES_QUEUED = 1 << 1, + + /** + * \brief Set, when dw::core::Widget::requisition is not up to date + * anymore. + * + * \todo Update, see RESIZE_QUEUED. + */ + NEEDS_RESIZE = 1 << 2, + + /** + * \brief Only used internally, set to enforce size allocation. + * + * In some cases, the size of a widget remains the same, but the + * children are allocated at different positions and in + * different sizes, so that a simple comparison of old and new + * allocation is insufficient. Therefore, this flag is set + * (indirectly, as ALLOCATE_QUEUED) in queueResize. + */ + NEEDS_ALLOCATE = 1 << 3, + + /** + * \todo Comment this. + */ + ALLOCATE_QUEUED = 1 << 4, + + /** + * \brief Set, when dw::core::Widget::extremes is not up to date + * anymore. + * + * \todo Update, see RESIZE_QUEUED. + */ + EXTREMES_CHANGED = 1 << 5, + + /** + * \brief Set, when a widget was already once allocated, + * + * The dw::Image widget uses this flag, see dw::Image::setBuffer. + */ + WAS_ALLOCATED = 1 << 6, + }; + + /** + * \brief Implementation which represents the whole widget. + * + * The only instance is set created needed. + */ + class WidgetImgRenderer: public style::StyleImage::ExternalWidgetImgRenderer + { + private: + Widget *widget; + + public: + inline WidgetImgRenderer (Widget *widget) { this->widget = widget; } + + bool readyToDraw (); + void getBgArea (int *x, int *y, int *width, int *height); + void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef); + style::Style *getStyle (); + void draw (int x, int y, int width, int height); + }; + + WidgetImgRenderer *widgetImgRenderer; + +private: + static bool adjustMinWidth; + + /** + * \brief The parent widget, NULL for top-level widgets. + */ + Widget *parent; + + /** + * \brief ... + */ + Widget *quasiParent; + + /** + * \brief The generating widget, NULL for top-level widgets, or if + * not set; in the latter case, the effective generator (see + * getGenerator) is the parent. + */ + Widget *generator; + + /** + * \brief The containing widget, equivalent to the "containing + * block" defined by CSS. May be NULL, in this case the viewport + * is used. + */ + Widget *container; + + style::Style *style; + + Flags flags; + + /** + * \brief Size_request() stores the result of the last call of + * size_request_impl(). + * + * Do not read this directly, but call size_request(). + */ + Requisition requisition; + + /** + * \brief Analogue to dw::core::Widget::requisition. + */ + Extremes extremes; + + /** + * \brief See dw::core::Widget::setBgColor(). + */ + style::Color *bgColor; + + /** + * \brief See dw::core::Widget::setButtonSensitive(). + */ + bool buttonSensitive; + + /** + * \brief See dw::core::Widget::setButtonSensitive(). + */ + bool buttonSensitiveSet; + + void queueResize (int ref, bool extremesChanged, bool fast); + inline void queueResizeFast (int ref, bool extremesChanged) + { queueResize (ref, extremesChanged, true); } + void actualQueueResize (int ref, bool extremesChanged, bool fast); + +public: + /** + * \brief This value is defined by the parent widget, and used for + * incremential resizing. + * + * See documentation for an explanation. + */ + int parentRef; + +protected: + + /** + * \brief The current allocation: size and position, always relative to the + * canvas. + */ + Allocation allocation; + + inline int getHeight () { return allocation.ascent + allocation.descent; } + inline int getContentWidth() { return allocation.width + - style->boxDiffWidth (); } + inline int getContentHeight() { return getHeight () + - style->boxDiffHeight (); } + + Layout *layout; + + /** + * \brief Space around the margin box. Allocation is extraSpace + + * margin + border + padding + contents; + */ + style::Box extraSpace; + + /*inline void printFlags () { + DBG_IF_RTFL { + char buf[10 * 3 - 1 + 1]; + snprintf (buf, sizeof (buf), "%s:%s:%s:%s:%s:%s:%s", + (flags & RESIZE_QUEUED) ? "Rq" : "--", + (flags & EXTREMES_QUEUED) ? "Eq" : "--", + (flags & NEEDS_RESIZE) ? "nR" : "--", + (flags & NEEDS_ALLOCATE) ? "nA" : "--", + (flags & ALLOCATE_QUEUED) ? "Aq" : "--", + (flags & EXTREMES_CHANGED) ? "Ec" : "--", + (flags & WAS_ALLOCATED) ? "wA" : "--"); + DBG_OBJ_SET_SYM ("flags", buf); + } + }*/ + + inline void printFlag (Flags f) { + DBG_IF_RTFL { + switch (f) { + case RESIZE_QUEUED: + DBG_OBJ_SET_SYM ("flags.RESIZE_QUEUED", + (flags & RESIZE_QUEUED) ? "true" : "false"); + break; + + case EXTREMES_QUEUED: + DBG_OBJ_SET_SYM ("flags.EXTREMES_QUEUED", + (flags & EXTREMES_QUEUED) ? "true" : "false"); + break; + + case NEEDS_RESIZE: + DBG_OBJ_SET_SYM ("flags.NEEDS_RESIZE", + (flags & NEEDS_RESIZE) ? "true" : "false"); + break; + + case NEEDS_ALLOCATE: + DBG_OBJ_SET_SYM ("flags.NEEDS_ALLOCATE", + (flags & NEEDS_ALLOCATE) ? "true" : "false"); + break; + + case ALLOCATE_QUEUED: + DBG_OBJ_SET_SYM ("flags.ALLOCATE_QUEUED", + (flags & ALLOCATE_QUEUED) ? "true" : "false"); + break; + + case EXTREMES_CHANGED: + DBG_OBJ_SET_SYM ("flags.EXTREMES_CHANGED", + (flags & EXTREMES_CHANGED) ? "true" : "false"); + break; + + case WAS_ALLOCATED: + DBG_OBJ_SET_SYM ("flags.WAS_ALLOCATED", + (flags & WAS_ALLOCATED) ? "true" : "false"); + break; + } + } + } + + inline void setFlags (Flags f) + { flags = (Flags)(flags | f); printFlag (f); } + inline void unsetFlags (Flags f) + { flags = (Flags)(flags & ~f); printFlag (f); } + + + inline void queueDraw () + { queueDrawArea (0, 0, allocation.width, getHeight()); } + void queueDrawArea (int x, int y, int width, int height); + inline void queueResize (int ref, bool extremesChanged) + { queueResize (ref, extremesChanged, false); } + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void sizeRequestImpl (Requisition *requisition) = 0; + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void getExtremesImpl (Extremes *extremes) = 0; + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void sizeAllocateImpl (Allocation *allocation); + + /** + * \brief Called after sizeAllocateImpl() to redraw necessary areas. + * By default the whole widget is redrawn. + */ + virtual void resizeDrawImpl () { queueDraw (); }; + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void markSizeChange (int ref); + + /** + * \brief See \ref dw-widget-sizes. + */ + virtual void markExtremesChange (int ref); + + int getMinWidth (Extremes *extremes, bool forceValue); + + virtual int getAvailWidthOfChild (Widget *child, bool forceValue); + virtual int getAvailHeightOfChild (Widget *child, bool forceValue); + virtual void correctRequisitionOfChild (Widget *child, + Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)); + void correctReqWidthOfChild (Widget *child, Requisition *requisition); + void correctReqHeightOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + virtual void correctExtremesOfChild (Widget *child, Extremes *extremes); + + virtual void containerSizeChangedForChildren (); + + virtual bool affectedByContainerSizeChange (); + virtual bool affectsSizeChangeContainerChild (Widget *child); + virtual bool usesAvailWidth (); + virtual bool usesAvailHeight (); + + virtual void notifySetAsTopLevel(); + virtual void notifySetParent(); + + virtual bool buttonPressImpl (EventButton *event); + virtual bool buttonReleaseImpl (EventButton *event); + virtual bool motionNotifyImpl (EventMotion *event); + virtual void enterNotifyImpl (EventCrossing *event); + virtual void leaveNotifyImpl (EventCrossing *event); + + inline char *addAnchor (const char* name) + { return layout->addAnchor (this, name); } + + inline char *addAnchor (const char* name, int y) + { return layout->addAnchor (this, name, y); } + + inline void changeAnchor (char* name, int y) + { layout->changeAnchor (this, name, y); } + + inline void removeAnchor (char* name) + { if (layout) layout->removeAnchor (this, name); } + + //inline void updateBgColor () { layout->updateBgColor (); } + + inline void setCursor (style::Cursor cursor) + { layout->setCursor (cursor); } +#if 0 + inline bool selectionButtonPress (Iterator *it, int charPos, int linkNo, + EventButton *event, bool withinContent) + { return layout->selectionState.buttonPress (it, charPos, linkNo, event); } + + inline bool selectionButtonRelease (Iterator *it, int charPos, int linkNo, + EventButton *event, bool withinContent) + { return layout->selectionState.buttonRelease (it, charPos, linkNo, event);} + + inline bool selectionButtonMotion (Iterator *it, int charPos, int linkNo, + EventMotion *event, bool withinContent) + { return layout->selectionState.buttonMotion (it, charPos, linkNo, event); } +#endif + inline bool selectionHandleEvent (SelectionState::EventType eventType, + Iterator *it, int charPos, int linkNo, + MousePositionEvent *event) + { return layout->selectionState.handleEvent (eventType, it, charPos, linkNo, + event); } + +private: + void *deleteCallbackData; + DW_Callback_t deleteCallbackFunc; + +public: + inline void setDeleteCallback(DW_Callback_t func, void *data) + { deleteCallbackFunc = func; deleteCallbackData = data; } + +private: + bool resizeIdleEntered () { return layout && layout->resizeIdleCounter; } + + void enterQueueResize () { if (layout) layout->queueResizeCounter++; } + void leaveQueueResize () { if (layout) layout->queueResizeCounter--; } + bool queueResizeEntered () { return layout && layout->queueResizeCounter; } + + void enterSizeAllocate () { if (layout) layout->sizeAllocateCounter++; } + void leaveSizeAllocate () { if (layout) layout->sizeAllocateCounter--; } + bool sizeAllocateEntered () { return layout && layout->sizeAllocateCounter; } + + void enterSizeRequest () { if (layout) layout->sizeRequestCounter++; } + void leaveSizeRequest () { if (layout) layout->sizeRequestCounter--; } + bool sizeRequestEntered () { return layout && layout->sizeRequestCounter; } + + void enterGetExtremes () { if (layout) layout->getExtremesCounter++; } + void leaveGetExtremes () { if (layout) layout->getExtremesCounter--; } + bool getExtremesEntered () { return layout && layout->getExtremesCounter; } + + +public: + static int CLASS_ID; + + inline static void setAdjustMinWidth (bool adjustMinWidth) + { Widget::adjustMinWidth = adjustMinWidth; } + + Widget (); + ~Widget (); + + inline bool resizeQueued () { return flags & RESIZE_QUEUED; } + inline bool extremesQueued () { return flags & EXTREMES_QUEUED; } + inline bool needsResize () { return flags & NEEDS_RESIZE; } + inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; } + inline bool allocateQueued () { return flags & ALLOCATE_QUEUED; } + inline bool extremesChanged () { return flags & EXTREMES_CHANGED; } + inline bool wasAllocated () { return flags & WAS_ALLOCATED; } + + void setParent (Widget *parent); + void setQuasiParent (Widget *quasiParent); + + void setGenerator (Widget *generator) { this->generator = generator; } + + inline style::Style *getStyle () { return style; } + /** \todo I do not like this. */ + inline Allocation *getAllocation () { return &allocation; } + + inline int boxOffsetX () + { return extraSpace.left + getStyle()->boxOffsetX (); } + inline int boxRestWidth () + { return extraSpace.right + getStyle()->boxRestWidth (); } + inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); } + inline int boxOffsetY () + { return extraSpace.top + getStyle()->boxOffsetY (); } + inline int boxRestHeight () + { return extraSpace.bottom + getStyle()->boxRestHeight (); } + inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } + + void sizeRequest (Requisition *requisition); + void getExtremes (Extremes *extremes); + void sizeAllocate (Allocation *allocation); + + int getAvailWidth (bool forceValue); + int getAvailHeight (bool forceValue); + virtual bool getAdjustMinWidth () { return Widget::adjustMinWidth; } + void correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremes (Extremes *extremes); + int calcWidth (style::Length cssValue, int refWidth, Widget *refWidget, + int limitMinWidth, bool forceValue); + void calcFinalWidth (style::Style *style, int refWidth, Widget *refWidget, + int limitMinWidth, bool forceValue, int *finalWidth); + int calcHeight (style::Length cssValue, bool usePercentage, int refHeight, + Widget *refWidget, bool forceValue); + + virtual int applyPerWidth (int containerWidth, style::Length perWidth); + virtual int applyPerHeight (int containerHeight, style::Length perHeight); + + virtual bool isBlockLevel (); + virtual bool isPossibleContainer (); + + void containerSizeChanged (); + + bool intersects (Rectangle *area, Rectangle *intersection); + + /** Area is given in widget coordinates. */ + virtual void draw (View *view, Rectangle *area) = 0; + + bool buttonPress (EventButton *event); + bool buttonRelease (EventButton *event); + bool motionNotify (EventMotion *event); + void enterNotify (EventCrossing *event); + void leaveNotify (EventCrossing *event); + + virtual void setStyle (style::Style *style); + void setBgColor (style::Color *bgColor); + style::Color *getBgColor (); + + void drawBox (View *view, style::Style *style, Rectangle *area, + int x, int y, int width, int height, bool inverse); + void drawWidgetBox (View *view, Rectangle *area, bool inverse); + void drawSelected (View *view, Rectangle *area); + + void setButtonSensitive (bool buttonSensitive); + inline bool isButtonSensitive () { return buttonSensitive; } + + inline Widget *getParent () { return parent; } + inline Widget *getContainer () { return container; } + Widget *getTopLevel (); + int getLevel (); + int getGeneratorLevel (); + Widget *getNearestCommonAncestor (Widget *otherWidget); + + inline Widget *getGenerator () { return generator ? generator : parent; } + + inline Layout *getLayout () { return layout; } + + virtual Widget *getWidgetAtPoint (int x, int y, int level); + + void scrollTo (HPosition hpos, VPosition vpos); + void scrollTo (HPosition hpos, VPosition vpos, + int x, int y, int width, int height); + + void getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad); + + /** + * \brief Return an iterator for this widget. + * + * \em mask can narrow the types returned by the iterator, this can + * enhance performance quite much, e.g. when only searching for child + * widgets. + * + * With \em atEnd == false, the iterator starts \em before the beginning, + * i.e. the first call of dw::core::Iterator::next will let the iterator + * point on the first piece of contents. Likewise, With \em atEnd == true, + * the iterator starts \em after the last piece of contents, call + * dw::core::Iterator::prev in this case. + */ + virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; + virtual void removeChild (Widget *child); +}; + +void splitHeightPreserveAscent (int height, int *ascent, int *descent); +void splitHeightPreserveDescent (int height, int *ascent, int *descent); + +} // namespace core +} // namespace dw + +#endif // __DW_WIDGET_HH__ |