GWidget: Add some new child z-ordering facilities.

- child_at(Point)
- move_to_front()
- move_to_back()
- is_frontmost()
- is_backmost()

This patch also makes it possible to receive the mouse event that triggers
a context menu before the context menu is shown. I'm not sure this is the
best design for context menus but it works for now.
This commit is contained in:
Andreas Kling 2019-04-16 03:47:55 +02:00
parent d31b47b371
commit 04500c1ae2
3 changed files with 69 additions and 7 deletions

View file

@ -18,6 +18,7 @@ public:
virtual void event(CEvent&);
Vector<CObject*>& children() { return m_children; }
const Vector<CObject*>& children() const { return m_children; }
CObject* parent() { return m_parent; }
const CObject* parent() const { return m_parent; }

View file

@ -178,6 +178,8 @@ void GWidget::handle_mousedown_event(GMouseEvent& event)
set_focus(true);
if (event.button() == GMouseButton::Right) {
if (m_context_menu) {
if (m_context_menu_mode == ContextMenuMode::PassthroughMouseEvent)
mousedown_event(event);
m_context_menu->popup(screen_relative_rect().location().translated(event.position()));
return;
}
@ -294,19 +296,26 @@ Rect GWidget::screen_relative_rect() const
return window_relative_rect().translated(window()->position());
}
GWidget::HitTestResult GWidget::hit_test(int x, int y)
GWidget* GWidget::child_at(const Point& point)
{
if (is_greedy_for_hits())
return { this, x, y };
for (int i = children().size() - 1; i >= 0; --i) {
if (!children()[i]->is_widget())
continue;
auto& child = *(GWidget*)children()[i];
if (!child.is_visible())
continue;
if (child.relative_rect().contains(x, y))
return child.hit_test(x - child.relative_rect().x(), y - child.relative_rect().y());
if (child.relative_rect().contains(point))
return &child;
}
return nullptr;
}
GWidget::HitTestResult GWidget::hit_test(int x, int y)
{
if (is_greedy_for_hits())
return { this, x, y };
if (auto* child = child_at({ x, y }))
return child->hit_test(x - child->x(), y - child->y());
return { this, x, y };
}
@ -426,9 +435,50 @@ void GWidget::set_enabled(bool enabled)
update();
}
void GWidget::set_context_menu(OwnPtr<GMenu>&& context_menu)
void GWidget::set_context_menu(OwnPtr<GMenu>&& context_menu, ContextMenuMode mode)
{
// FIXME: Support switching context menus.
ASSERT(!m_context_menu);
m_context_menu = move(context_menu);
m_context_menu_mode = mode;
}
void GWidget::move_to_front()
{
auto* parent = parent_widget();
if (!parent)
return;
if (parent->children().size() == 1)
return;
parent->children().remove_first_matching([this] (auto& entry) { return entry == this; });
parent->children().append(this);
parent_widget()->update();
}
void GWidget::move_to_back()
{
auto* parent = parent_widget();
if (!parent)
return;
if (parent->children().size() == 1)
return;
parent->children().remove_first_matching([this] (auto& entry) { return entry == this; });
parent->children().prepend(this);
parent_widget()->update();
}
bool GWidget::is_frontmost() const
{
auto* parent = parent_widget();
if (!parent)
return true;
return parent->children().last() == this;
}
bool GWidget::is_backmost() const
{
auto* parent = parent_widget();
if (!parent)
return true;
return parent->children().first() == this;
}

View file

@ -42,8 +42,10 @@ public:
bool is_enabled() const { return m_enabled; }
void set_enabled(bool);
enum class ContextMenuMode { SwallowMouseEvent, PassthroughMouseEvent };
const GMenu* context_menu() const { return m_context_menu.ptr(); }
void set_context_menu(OwnPtr<GMenu>&&);
void set_context_menu(OwnPtr<GMenu>&&, ContextMenuMode = ContextMenuMode::SwallowMouseEvent);
virtual void event(CEvent&) override;
virtual void paint_event(GPaintEvent&);
@ -95,6 +97,8 @@ public:
};
HitTestResult hit_test(int x, int y);
GWidget* child_at(const Point&);
virtual const char* class_name() const override { return "GWidget"; }
void set_relative_rect(const Rect&);
@ -165,6 +169,12 @@ public:
bool is_greedy_for_hits() const { return m_greedy_for_hits; }
void set_greedy_for_hits(bool b) { m_greedy_for_hits = b; }
void move_to_front();
void move_to_back();
bool is_frontmost() const;
bool is_backmost() const;
private:
virtual bool is_widget() const final { return true; }
@ -196,4 +206,5 @@ private:
CElapsedTimer m_click_clock;
OwnPtr<GMenu> m_context_menu;
ContextMenuMode m_context_menu_mode { ContextMenuMode::SwallowMouseEvent };
};