Make PopupMenu/Panel shadows properly visible again

This commit is contained in:
Michael Alexsander 2024-04-29 01:09:58 -03:00
parent 0f20e67d8d
commit f7f6432af6
No known key found for this signature in database
GPG key ID: A9C91EE110F4EABA
8 changed files with 336 additions and 71 deletions

View file

@ -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) {
@ -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;
@ -239,17 +256,77 @@ 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;
}
@ -257,26 +334,68 @@ void PopupPanel::_update_child_rects() {
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.");
}
}
} 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;
}
}
@ -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);
}