Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make PopupMenu/Panel shadows properly visible again #91333

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/classes/PopupMenu.xml
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,8 @@
<member name="system_menu_id" type="int" setter="set_system_menu" getter="get_system_menu" enum="NativeMenu.SystemMenus" default="0">
If set to one of the values of [enum NativeMenu.SystemMenus], this [PopupMenu] is bound to the special system menu. Only one [PopupMenu] can be bound to each special menu at a time.
</member>
<member name="transparent" type="bool" setter="set_flag" getter="get_flag" overrides="Window" default="true" />
<member name="transparent_bg" type="bool" setter="set_transparent_background" getter="has_transparent_background" overrides="Viewport" default="true" />
</members>
<signals>
<signal name="id_focused">
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/PopupPanel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
</description>
<tutorials>
</tutorials>
<members>
<member name="transparent" type="bool" setter="set_flag" getter="get_flag" overrides="Window" default="true" />
<member name="transparent_bg" type="bool" setter="set_transparent_background" getter="has_transparent_background" overrides="Viewport" default="true" />
</members>
<theme_items>
<theme_item name="panel" data_type="style" type="StyleBox">
[StyleBox] for the background panel.
Expand Down
150 changes: 136 additions & 14 deletions scene/gui/popup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@

#include "popup.h"

#include "core/config/engine.h"
#include "core/os/keyboard.h"
#include "scene/gui/panel.h"
#include "scene/resources/style_box_flat.h"
#include "scene/theme/theme_db.h"

void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
Expand Down Expand Up @@ -222,6 +221,24 @@ Popup::Popup() {
Popup::~Popup() {
}

void PopupPanel::_input_from_window(const Ref<InputEvent> &p_event) {
if (p_event.is_valid()) {
if (!get_flag(FLAG_POPUP)) {
return;
}

Ref<InputEventMouseButton> b = p_event;
// Hide it if the shadows have been clicked.
if (b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT && !panel->get_global_rect().has_point(b->get_position())) {
_close_pressed();
}
} else {
WARN_PRINT_ONCE("PopupPanel has received an invalid InputEvent. Consider filtering out invalid events.");
}

Popup::_input_from_window(p_event);
}

Size2 PopupPanel::_get_contents_minimum_size() const {
Size2 ms;

Expand All @@ -239,44 +256,146 @@ Size2 PopupPanel::_get_contents_minimum_size() const {
ms = cms.max(ms);
}

// Take shadows into account.
ms.width += panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT);
ms.height += panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM);

return ms + theme_cache.panel_style->get_minimum_size();
}

void PopupPanel::_update_child_rects() {
Rect2i PopupPanel::_popup_adjust_rect() const {
Rect2i current = Popup::_popup_adjust_rect();
if (current == Rect2i()) {
return current;
}

pre_popup_rect = current;

_update_shadow_offsets();
_update_child_rects();

if (is_layout_rtl()) {
current.position -= Vector2(ABS(panel->get_offset(SIDE_RIGHT)), panel->get_offset(SIDE_TOP)) * get_content_scale_factor();
} else {
current.position -= Vector2(panel->get_offset(SIDE_LEFT), panel->get_offset(SIDE_TOP)) * get_content_scale_factor();
}
current.size += Vector2(panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT), panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM)) * get_content_scale_factor();

return current;
}

void PopupPanel::_update_shadow_offsets() const {
if (!DisplayServer::get_singleton()->is_window_transparency_available() && !is_embedded()) {
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
return;
}

const Ref<StyleBoxFlat> sb = theme_cache.panel_style;
if (sb.is_null()) {
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
return;
}

const int shadow_size = sb->get_shadow_size();
if (shadow_size == 0) {
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
return;
}

// Offset the background panel so it leaves space inside the window for the shadows to be drawn.
const Point2 shadow_offset = sb->get_shadow_offset();
if (is_layout_rtl()) {
panel->set_offset(SIDE_LEFT, shadow_size + shadow_offset.x);
panel->set_offset(SIDE_RIGHT, -shadow_size + shadow_offset.x);
} else {
panel->set_offset(SIDE_LEFT, shadow_size - shadow_offset.x);
panel->set_offset(SIDE_RIGHT, -shadow_size - shadow_offset.x);
}
panel->set_offset(SIDE_TOP, shadow_size - shadow_offset.y);
panel->set_offset(SIDE_BOTTOM, -shadow_size - shadow_offset.y);
}

void PopupPanel::_update_child_rects() const {
Vector2 cpos(theme_cache.panel_style->get_offset());
Vector2 panel_size = Vector2(get_size()) / get_content_scale_factor();
Vector2 csize = panel_size - theme_cache.panel_style->get_minimum_size();
cpos += Vector2(is_layout_rtl() ? -panel->get_offset(SIDE_RIGHT) : panel->get_offset(SIDE_LEFT), panel->get_offset(SIDE_TOP));

Vector2 csize = Vector2(get_size()) / get_content_scale_factor() - theme_cache.panel_style->get_minimum_size();
// Trim shadows.
csize.width -= panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT);
csize.height -= panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM);

for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c) {
if (!c || c == panel) {
continue;
}

if (c->is_set_as_top_level()) {
continue;
}

if (c == panel) {
c->set_position(Vector2());
c->set_size(panel_size);
} else {
c->set_position(cpos);
c->set_size(csize);
}
c->set_position(cpos);
c->set_size(csize);
}
}

void PopupPanel::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY:
case NOTIFICATION_ENTER_TREE: {
if (!Engine::get_singleton()->is_editor_hint() && !DisplayServer::get_singleton()->is_window_transparency_available() && !is_embedded()) {
Ref<StyleBoxFlat> sb = theme_cache.panel_style;
if (sb.is_valid() && (sb->get_shadow_size() > 0 || sb->get_corner_radius(CORNER_TOP_LEFT) > 0 || sb->get_corner_radius(CORNER_TOP_RIGHT) > 0 || sb->get_corner_radius(CORNER_BOTTOM_LEFT) > 0 || sb->get_corner_radius(CORNER_BOTTOM_RIGHT) > 0)) {
WARN_PRINT_ONCE("The current theme styles PopupPanel to have shadows and/or rounded corners, but those won't display correctly if 'display/window/per_pixel_transparency/allowed' isn't enabled in the Project Settings, nor if it isn't supported.");
}
}
YeldhamDev marked this conversation as resolved.
Show resolved Hide resolved
} break;

case NOTIFICATION_THEME_CHANGED: {
panel->add_theme_style_override(SceneStringName(panel), theme_cache.panel_style);

if (is_visible()) {
_update_shadow_offsets();
}

_update_child_rects();
} break;

case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
if (is_visible()) {
_update_shadow_offsets();
}
} break;

case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible()) {
// Remove the extra space used by the shadows, so they can be ignored when the popup is hidden.
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
_update_child_rects();

if (pre_popup_rect != Rect2i()) {
set_position(pre_popup_rect.position);
set_size(pre_popup_rect.size);

pre_popup_rect = Rect2i();
}
} else if (pre_popup_rect == Rect2i()) {
// The popup was made visible directly (without `popup_*()`), so just update the offsets without touching the rect.
_update_shadow_offsets();
_update_child_rects();
}
} break;

case NOTIFICATION_WM_SIZE_CHANGED: {
_update_child_rects();

if (is_visible()) {
const Vector2i offsets = Vector2i(panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT), panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM));
// Check if the size actually changed.
if (pre_popup_rect.size + offsets != get_size()) {
// Play safe, and stick with the new size.
pre_popup_rect = Rect2i();
}
}
} break;
}
}
Expand All @@ -286,6 +405,9 @@ void PopupPanel::_bind_methods() {
}

PopupPanel::PopupPanel() {
set_flag(FLAG_TRANSPARENT, true);

panel = memnew(Panel);
panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
add_child(panel, false, INTERNAL_MODE_FRONT);
}
9 changes: 8 additions & 1 deletion scene/gui/popup.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,15 @@ class PopupPanel : public Popup {
Ref<StyleBox> panel_style;
} theme_cache;

mutable Rect2i pre_popup_rect;

protected:
void _update_child_rects();
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;

virtual Rect2i _popup_adjust_rect() const override;

void _update_shadow_offsets() const;
void _update_child_rects() const;

void _notification(int p_what);
static void _bind_methods();
Expand Down
Loading