5#include "fifechan/widgets/menupopup.hpp"
10#include <unordered_map>
13#include "fifechan/color.hpp"
14#include "fifechan/focushandler.hpp"
15#include "fifechan/font.hpp"
16#include "fifechan/graphics.hpp"
17#include "fifechan/widgets/menubar.hpp"
18#include "fifechan/widgets/menuitem.hpp"
19#include "fifechan/widgets/modalbackdrop.hpp"
26 constexpr int GAP_ICON_CAPTION = 6;
27 constexpr int GAP_CAPTION_SHORTCUT = 16;
28 constexpr int GAP_SHORTCUT_ARROW = 10;
31 void MenuPopup::layoutItems()
34 if (children.empty()) {
40 if (font ==
nullptr) {
48 std::unordered_map<MenuItem*, MenuItemMetrics> metricsMap;
50 for (
auto* child : children) {
51 if (child ==
nullptr) {
55 auto* mi =
dynamic_cast<MenuItem*
>(child);
60 if (mi->getType() == MenuItem::Type::Separator) {
64 auto m = mi->measure(*font);
69 cols.iconW = std::max(cols.iconW, m.iconW);
70 cols.captionW = std::max(cols.captionW, m.captionW);
71 cols.shortcutW = std::max(cols.shortcutW, m.shortcutW);
72 cols.arrowW = std::max(cols.arrowW, m.arrowW);
76 int const gapIconCaption = cols.iconW > 0 ? GAP_ICON_CAPTION : 0;
77 int const gapCaptionShortcut = (cols.captionW > 0 && cols.shortcutW > 0) ? GAP_CAPTION_SHORTCUT : 0;
78 int const gapShortcutArrow = (cols.shortcutW > 0 && cols.arrowW > 0) ? GAP_SHORTCUT_ARROW : 0;
81 int const contentW = cols.iconW + gapIconCaption + cols.captionW + gapCaptionShortcut + cols.shortcutW +
82 gapShortcutArrow + cols.arrowW;
87 int const xCaption = xIcon + cols.iconW + gapIconCaption;
88 int const xShortcut = xCaption + cols.captionW + gapCaptionShortcut;
89 int const xArrow = xShortcut + cols.shortcutW + gapShortcutArrow;
92 ColumnLayout layout{};
94 layout.xCaption = xCaption;
95 layout.xShortcut = xShortcut;
96 layout.xArrow = xArrow;
103 for (
auto* child : children) {
104 if (child ==
nullptr) {
108 int h = child->getHeight();
110 if (
auto* mi =
dynamic_cast<MenuItem*
>(child)) {
111 auto it = metricsMap.find(mi);
112 if (it != metricsMap.end()) {
114 h = it->second.height;
119 child->setPosition(0, y);
120 child->setSize(contentW, h);
123 if (
auto* mi =
dynamic_cast<MenuItem*
>(child)) {
124 mi->layoutColumns(layout);
134 int const rightBorder =
137 int const bottomBorder =
143 int const popupWidth = contentW + contentPaddingW;
144 int const popupHeight = contentH + contentPaddingH;
147 setSize(popupWidth, popupHeight);
182 if (mParentMenuItem !=
nullptr) {
183 topContainer =
dynamic_cast<Container*
>(mParentMenuItem->getTop());
185 if (topContainer ==
nullptr) {
191 if (currentParent !=
nullptr && currentParent != topContainer) {
192 if (
auto* parentContainer =
dynamic_cast<Container*
>(currentParent)) {
193 parentContainer->remove(
this);
199 if (topContainer !=
nullptr && currentParent != topContainer) {
200 topContainer->
add(
this);
207 if (topContainer !=
nullptr) {
218 mModalScope = std::make_unique<FocusHandler::ModalScope>(
_getFocusHandler(),
this,
this);
223 if (topContainer !=
nullptr) {
228 topContainer->
add(backdrop);
233 mBackdrop = backdrop;
235 }
else if (mParentMenu !=
nullptr) {
247 if (mParentMenuItem !=
nullptr) {
248 mParentMenuItem->requestFocus();
252 if (mOpenChild !=
nullptr) {
254 mOpenChild =
nullptr;
258 if (mBackdrop !=
nullptr) {
259 if (mBackdrop->getParent() !=
nullptr) {
260 if (
auto* parentContainer =
dynamic_cast<Container*
>(mBackdrop->getParent())) {
261 parentContainer->remove(mBackdrop);
269 if (mModalScope !=
nullptr) {
282 mParentMenuItem = parent;
287 return mParentMenuItem;
297 mParentMenu = parent;
322 auto* mi =
dynamic_cast<MenuItem*
>(item);
323 if (mi !=
nullptr && mi->getType() != MenuItem::Type::Separator) {
324 mi->addActionListener(
this);
332 sep->setType(MenuItem::Type::Separator);
365 int const relx =
event.getX();
366 int const rely =
event.getY();
383 bool clickHitsMenuBar =
false;
385 if (topContainer !=
nullptr) {
388 if (child ==
nullptr || child ==
this) {
394 if (absx >= cx && absx < cx + child->
getWidth() && absy >= cy && absy < cy + child->
getHeight()) {
395 if (
dynamic_cast<MenuBar*
>(child) !=
nullptr ||
dynamic_cast<MenuItem*
>(child) !=
nullptr) {
396 clickHitsMenuBar =
true;
403 if (clickHitsMenuBar) {
420 Widget* src =
event.getSource();
423 for (
auto* child : children) {
426 child->requestFocus();
430 if (
auto* mi =
dynamic_cast<MenuItem*
>(child)) {
432 if (submenu !=
nullptr) {
434 if (mOpenChild !=
nullptr && mOpenChild != submenu) {
444 mi->getAbsolutePosition(ax, ay);
445 int const sx = ax + mi->getWidth();
447 submenu->
show(sx, sy);
448 mOpenChild = submenu;
464 Key const key =
event.getKey();
466 if (key.
getValue() == Key::Escape) {
473 if (mParentMenuItem !=
nullptr) {
476 if (children.empty()) {
480 mHoverIndex = std::max(mHoverIndex, 0);
484 (mHoverIndex - 1 +
static_cast<int>(children.size())) %
static_cast<int>(children.size());
486 mHoverIndex = (mHoverIndex + 1) %
static_cast<int>(children.size());
491 for (
auto* target : children) {
492 if (i == mHoverIndex) {
493 if (target !=
nullptr) {
494 target->requestFocus();
507 if (mHoverIndex >= 0) {
509 for (
auto* child : children) {
510 if (i == mHoverIndex) {
511 if (
auto* mi =
dynamic_cast<MenuItem*
>(child)) {
512 if (mi->getSubmenu() !=
nullptr) {
518 mi->getAbsolutePosition(ax, ay);
519 submenu->
show(ax + mi->getWidth(), ay);
520 mOpenChild = submenu;
533 if (mParentMenu !=
nullptr) {
557 auto* source =
dynamic_cast<MenuItem*
>(
event.getSource());
558 if (source ==
nullptr) {
563 if (source->getType() == MenuItem::Type::Checkable) {
564 source->setChecked(!source->isChecked());
Represents an action trigger (e.g., button click).
void draw(Graphics *graphics) override
Draws the widget.
virtual void setLayout(LayoutPolicy policy)
Sets the layout of the container.
void resizeToContent(bool recursion=true) override
Resize this container to fit its children.
virtual void add(Widget *widget)
Adds a widget to the container.
Widget * getChild(unsigned int index) const
Gets child by index.
virtual void setOpaque(bool opaque)
Sets the container to be opaque or not.
Base class for all GUI event objects.
Abstract interface providing primitive drawing functions (lines, rectangles, etc.).
virtual void setColor(Color const &color)=0
Sets the color to use when drawing.
virtual void fillRectangle(Rectangle const &rectangle)=0
Draws a filled rectangle.
Represents a keyboard key or character code.
int getValue() const
Gets the value of the key.
A Transparent fullscreen backdrop.
Represents a mouse event.
Represents a rectangular area (X, Y, Width, Height).
Used replacement tokens by configure_file():