diff options
| author | Yuqian Yang <crupest@crupest.life> | 2025-12-06 20:06:10 +0800 |
|---|---|---|
| committer | Yuqian Yang <crupest@crupest.life> | 2025-12-07 19:52:05 +0800 |
| commit | 4a30bf58a48ed6f31f4c53473e8de70a8cd819da (patch) | |
| tree | 32aab619013cbe2d0557a212832ba24833d2cc36 | |
| parent | 9a87e5cf786f3e8fddc933136d210edd4ef72c89 (diff) | |
| download | cru-4a30bf58a48ed6f31f4c53473e8de70a8cd819da.tar.gz cru-4a30bf58a48ed6f31f4c53473e8de70a8cd819da.tar.bz2 cru-4a30bf58a48ed6f31f4c53473e8de70a8cd819da.zip | |
Fix SDL popup window.
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | demos/main/main.cpp | 2 | ||||
| -rw-r--r-- | include/cru/base/Guard.h | 18 | ||||
| -rw-r--r-- | include/cru/platform/gui/sdl/Window.h | 1 | ||||
| -rw-r--r-- | include/cru/ui/controls/Button.h | 1 | ||||
| -rw-r--r-- | include/cru/ui/controls/CheckBox.h | 1 | ||||
| -rw-r--r-- | include/cru/ui/controls/Control.h | 7 | ||||
| -rw-r--r-- | include/cru/ui/controls/IconButton.h | 1 | ||||
| -rw-r--r-- | include/cru/ui/controls/Window.h | 4 | ||||
| -rw-r--r-- | src/platform/gui/sdl/OpenGLRenderer.cpp | 6 | ||||
| -rw-r--r-- | src/platform/gui/sdl/Window.cpp | 48 | ||||
| -rw-r--r-- | src/ui/components/Menu.cpp | 3 | ||||
| -rw-r--r-- | src/ui/controls/Button.cpp | 2 | ||||
| -rw-r--r-- | src/ui/controls/CheckBox.cpp | 2 | ||||
| -rw-r--r-- | src/ui/controls/Control.cpp | 16 | ||||
| -rw-r--r-- | src/ui/controls/ControlHost.cpp | 5 | ||||
| -rw-r--r-- | src/ui/controls/IconButton.cpp | 2 | ||||
| -rw-r--r-- | src/ui/controls/TextHostControlService.cpp | 2 | ||||
| -rw-r--r-- | src/ui/controls/Window.cpp | 20 |
19 files changed, 102 insertions, 40 deletions
@@ -8,5 +8,6 @@ build build-clang build-dynamic +build-asan compile_commands.json diff --git a/demos/main/main.cpp b/demos/main/main.cpp index 71c084ec..cef3e44d 100644 --- a/demos/main/main.cpp +++ b/demos/main/main.cpp @@ -64,5 +64,7 @@ int main() { window.GetNativeWindow()->SetVisibility( cru::platform::gui::WindowVisibilityType::Show); + application->SetQuitOnAllWindowClosed(true); + return application->Run(); } diff --git a/include/cru/base/Guard.h b/include/cru/base/Guard.h index ae120f5a..6097b4d3 100644 --- a/include/cru/base/Guard.h +++ b/include/cru/base/Guard.h @@ -46,26 +46,21 @@ inline AutoFreePtr<T> MakeAutoFree(T* ptr) { template <typename T, typename Destructor> class AutoDestruct { public: - AutoDestruct() : value_(std::nullopt), auto_destruct_(false) {} + AutoDestruct() : value_(std::nullopt) {} - explicit AutoDestruct(T value, bool auto_destruct = true) - : value_(std::move(value)), auto_destruct_(auto_destruct) {} + explicit AutoDestruct(T value) : value_(std::move(value)) {} CRU_DELETE_COPY(AutoDestruct) AutoDestruct(AutoDestruct&& other) noexcept - : value_(std::move(other.value_)), auto_destruct_(other.auto_destruct_) { + : value_(std::move(other.value_)) { other.value_ = std::nullopt; - other.auto_destruct_ = false; } AutoDestruct& operator=(AutoDestruct&& other) noexcept { if (this != &other) { DoDestruct(); - value_ = other.value_; - auto_destruct_ = other.auto_destruct_; - other.value_ = std::nullopt; - other.auto_destruct_ = false; + value_.swap(other.value_); } return *this; } @@ -96,7 +91,6 @@ class AutoDestruct { CheckValid(); auto value = std::move(*value_); value_ = std::nullopt; - auto_destruct_ = false; return value; } @@ -109,13 +103,13 @@ class AutoDestruct { private: void DoDestruct() { - if (auto_destruct_ && value_) { + if (value_) { Destructor{}(*value_); + value_ = std::nullopt; } } private: std::optional<T> value_; - bool auto_destruct_; }; } // namespace cru diff --git a/include/cru/platform/gui/sdl/Window.h b/include/cru/platform/gui/sdl/Window.h index 48d7c70a..5b8b17a2 100644 --- a/include/cru/platform/gui/sdl/Window.h +++ b/include/cru/platform/gui/sdl/Window.h @@ -104,6 +104,7 @@ class SdlWindow : public SdlResource, public virtual INativeWindow { private: SdlUiApplication* application_; SDL_Window* sdl_window_; + bool sdl_is_popup_; SDL_WindowID sdl_window_id_; Rect client_rect_; SdlWindow* parent_; diff --git a/include/cru/ui/controls/Button.h b/include/cru/ui/controls/Button.h index 80d21d08..5ae3de49 100644 --- a/include/cru/ui/controls/Button.h +++ b/include/cru/ui/controls/Button.h @@ -14,7 +14,6 @@ class CRU_UI_API Button : public SingleChildControl<render::BorderRenderObject>, public: Button(); - ~Button() override; public: helper::ClickState GetClickState() override { diff --git a/include/cru/ui/controls/CheckBox.h b/include/cru/ui/controls/CheckBox.h index 2e4685d5..16fe0725 100644 --- a/include/cru/ui/controls/CheckBox.h +++ b/include/cru/ui/controls/CheckBox.h @@ -15,7 +15,6 @@ class CRU_UI_API CheckBox : public Control, static constexpr auto kControlName = "CheckBox"; CheckBox(); - ~CheckBox() override; render::RenderObject* GetRenderObject() override { return &container_render_object_; diff --git a/include/cru/ui/controls/Control.h b/include/cru/ui/controls/Control.h index 8cd7a375..aa1cef8a 100644 --- a/include/cru/ui/controls/Control.h +++ b/include/cru/ui/controls/Control.h @@ -12,6 +12,11 @@ namespace cru::ui::controls { +struct ControlHostChangeEventArgs { + ControlHost* old_host; + ControlHost* new_host; +}; + /** * \remarks If you want to write a new control. You should override following * methods: @@ -143,6 +148,8 @@ class CRU_UI_API Control : public Object, CRU_DEFINE_ROUTED_EVENT(GainFocus, events::FocusChangeEventArgs) CRU_DEFINE_ROUTED_EVENT(LoseFocus, events::FocusChangeEventArgs) + CRU_DEFINE_EVENT(ControlHostChange, const ControlHostChangeEventArgs&) + //*************** region: tree *************** protected: virtual void OnParentChanged(Control* old_parent, Control* new_parent); diff --git a/include/cru/ui/controls/IconButton.h b/include/cru/ui/controls/IconButton.h index b061bc87..5b0f899d 100644 --- a/include/cru/ui/controls/IconButton.h +++ b/include/cru/ui/controls/IconButton.h @@ -21,7 +21,6 @@ class CRU_UI_API IconButton : public Control, public: IconButton(); IconButton(std::string_view icon_svg_path_data_string, const Rect& view_port); - ~IconButton() override; render::RenderObject* GetRenderObject() override { return &container_render_object_; diff --git a/include/cru/ui/controls/Window.h b/include/cru/ui/controls/Window.h index 131d68a8..355b03a9 100644 --- a/include/cru/ui/controls/Window.h +++ b/include/cru/ui/controls/Window.h @@ -24,8 +24,9 @@ class CRU_UI_API Window Window(); - static Window* CreatePopup(); + static Window* CreatePopup(Control* attached_control); + Control* GetAttachedControl(); void SetAttachedControl(Control* control); platform::gui::INativeWindow* GetNativeWindow(); @@ -36,6 +37,7 @@ class CRU_UI_API Window std::unique_ptr<ControlHost> control_host_; Control* attached_control_; + EventHandlerRevokerGuard parent_window_guard_; EventHandlerRevokerListGuard gain_focus_on_create_and_destroy_when_lose_focus_event_guard_; diff --git a/src/platform/gui/sdl/OpenGLRenderer.cpp b/src/platform/gui/sdl/OpenGLRenderer.cpp index dbd59db2..ae5ff957 100644 --- a/src/platform/gui/sdl/OpenGLRenderer.cpp +++ b/src/platform/gui/sdl/OpenGLRenderer.cpp @@ -114,10 +114,6 @@ SdlOpenGLRenderer::SdlOpenGLRenderer(SdlWindow* window, int width, int height) { } SdlOpenGLRenderer::~SdlOpenGLRenderer() { - glad_gl_context_.DeleteBuffers(1, &gl_vertex_buffer_); - glad_gl_context_.DeleteBuffers(1, &gl_element_buffer_); - glad_gl_context_.DeleteTextures(1, &gl_texture_); - if (cairo_) { cairo_destroy(cairo_); cairo_ = nullptr; @@ -216,7 +212,7 @@ GLuint SdlOpenGLRenderer::CreateGLProgram() { auto check_program = [this](std::string_view name, GLuint program) { int success; char infoLog[512]; - glad_gl_context_.GetProgramiv(program, GL_COMPILE_STATUS, &success); + glad_gl_context_.GetProgramiv(program, GL_LINK_STATUS, &success); if (!success) { glad_gl_context_.GetProgramInfoLog(program, 512, nullptr, infoLog); CruLogError(kLogTag, "Failed to link OpenGL {} program: {}", name, diff --git a/src/platform/gui/sdl/Window.cpp b/src/platform/gui/sdl/Window.cpp index e5de9310..d301efa9 100644 --- a/src/platform/gui/sdl/Window.cpp +++ b/src/platform/gui/sdl/Window.cpp @@ -1,5 +1,5 @@ #include "cru/platform/gui/sdl/Window.h" -#include "cru/base/Base.h" +#include "cru/base/log/Logger.h" #include "cru/platform/Base.h" #include "cru/platform/GraphicsBase.h" #include "cru/platform/graphics/NullPainter.h" @@ -21,9 +21,16 @@ namespace cru::platform::gui::sdl { +namespace { +bool IsWayland() { + return SDL_GetCurrentVideoDriver() == std::string_view("wayland"); +} +} // namespace + SdlWindow::SdlWindow(SdlUiApplication* application) : application_(application), sdl_window_(nullptr), + sdl_is_popup_(false), sdl_window_id_(0), client_rect_(100, 100, 400, 200), parent_(nullptr) { @@ -237,12 +244,22 @@ void SdlWindow::DoCreateWindow() { flags |= SDL_WINDOW_OPENGL; #endif - if (style_.Has(WindowStyleFlags::NoCaptionAndBorder)) { + auto no_border = style_.Has(WindowStyleFlags::NoCaptionAndBorder); + if (no_border) { flags |= SDL_WINDOW_BORDERLESS; } - sdl_window_ = SDL_CreateWindow(title_.c_str(), client_rect_.width, - client_rect_.height, flags); + if (no_border && parent_ && parent_->sdl_window_) { + flags |= SDL_WINDOW_POPUP_MENU; + sdl_window_ = SDL_CreatePopupWindow(parent_->sdl_window_, client_rect_.left, + client_rect_.top, client_rect_.width, + client_rect_.height, flags); + sdl_is_popup_ = true; + } else { + sdl_window_ = SDL_CreateWindow(title_.c_str(), client_rect_.width, + client_rect_.height, flags); + sdl_is_popup_ = false; + } if (!sdl_window_) { throw SdlException("Failed to create window."); @@ -255,10 +272,16 @@ void SdlWindow::DoCreateWindow() { CreateEvent_.Raise(nullptr); - CheckSdlReturn( - SDL_SetWindowPosition(sdl_window_, client_rect_.left, client_rect_.top)); + if (!IsWayland() || sdl_is_popup_) { + CheckSdlReturn(SDL_SetWindowPosition(sdl_window_, client_rect_.left, + client_rect_.top)); + } + + if (!IsWayland() || !sdl_is_popup_) { + CheckSdlReturn(SDL_SetWindowParent( + sdl_window_, parent_ == nullptr ? nullptr : parent_->sdl_window_)); + } - DoUpdateParent(); DoUpdateCursor(); #ifdef __unix @@ -270,8 +293,15 @@ void SdlWindow::DoCreateWindow() { void SdlWindow::DoUpdateClientRect() { assert(sdl_window_); - CheckSdlReturn( - SDL_SetWindowPosition(sdl_window_, client_rect_.left, client_rect_.top)); + + if (!IsWayland() || sdl_is_popup_) { + CheckSdlReturn(SDL_SetWindowPosition(sdl_window_, client_rect_.left, + client_rect_.top)); + } else { + CruLogWarn(kLogTag, + "Wayland doesn't support set position of non-popup window."); + } + CheckSdlReturn( SDL_SetWindowSize(sdl_window_, client_rect_.width, client_rect_.height)); CheckSdlReturn(SDL_SyncWindow(sdl_window_)); diff --git a/src/ui/components/Menu.cpp b/src/ui/components/Menu.cpp index c6be942a..ea1afc07 100644 --- a/src/ui/components/Menu.cpp +++ b/src/ui/components/Menu.cpp @@ -78,8 +78,7 @@ void Menu::AddTextItemAt(std::string text, Index index, PopupMenu::PopupMenu(controls::Control* attached_control) : attached_control_(attached_control) { menu_.SetOnItemClick([this](Index) { popup_->GetNativeWindow()->Close(); }); - popup_.reset(controls::Window::CreatePopup()); - popup_->SetAttachedControl(attached_control); + popup_.reset(controls::Window::CreatePopup(attached_control)); popup_->InsertChildAt(menu_.GetRootControl(), 0); } diff --git a/src/ui/controls/Button.cpp b/src/ui/controls/Button.cpp index 4d897f08..d38ad108 100644 --- a/src/ui/controls/Button.cpp +++ b/src/ui/controls/Button.cpp @@ -13,8 +13,6 @@ Button::Button() GetStyleRuleSet()->SetParent(std::move(default_button_style)); } -Button::~Button() = default; - void Button::ApplyBorderStyle(const style::ApplyBorderStyleInfo& style) { GetContainerRenderObject()->ApplyBorderStyle(style); } diff --git a/src/ui/controls/CheckBox.cpp b/src/ui/controls/CheckBox.cpp index a6cac1a1..3090505e 100644 --- a/src/ui/controls/CheckBox.cpp +++ b/src/ui/controls/CheckBox.cpp @@ -17,8 +17,6 @@ CheckBox::CheckBox() [this](const helper::ClickEventArgs&) { Toggle(); }); } -CheckBox::~CheckBox() {} - void CheckBox::SetChecked(bool checked) { checked_ = checked; checked_change_event_.Raise(checked); diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp index 548c9726..57937da2 100644 --- a/src/ui/controls/Control.cpp +++ b/src/ui/controls/Control.cpp @@ -111,6 +111,14 @@ void Control::InsertChildAt(Control* control, Index index) { OnChildInserted(control, index); if (host_) { + control->TraverseDescendents( + [this](Control* control) { + control->ControlHostChangeEvent_.Raise({nullptr, host_}); + }, + true); + } + + if (host_) { host_->ScheduleRelayout(); } } @@ -132,6 +140,14 @@ void Control::RemoveChildAt(Index index) { OnChildRemoved(control, index); if (host_) { + control->TraverseDescendents( + [this](Control* control) { + control->ControlHostChangeEvent_.Raise({host_, nullptr}); + }, + true); + } + + if (host_) { host_->ScheduleRelayout(); } } diff --git a/src/ui/controls/ControlHost.cpp b/src/ui/controls/ControlHost.cpp index 1dabb26f..bfd7e580 100644 --- a/src/ui/controls/ControlHost.cpp +++ b/src/ui/controls/ControlHost.cpp @@ -22,6 +22,11 @@ ControlHost::ControlHost(Control* root_control) ControlHost::~ControlHost() { root_control_->TraverseDescendents( [this](Control* control) { control->host_ = nullptr; }, true); + root_control_->TraverseDescendents( + [this](Control* control) { + control->ControlHostChangeEvent_.Raise({this, nullptr}); + }, + true); } platform::gui::INativeWindow* ControlHost::GetNativeWindow() { diff --git a/src/ui/controls/IconButton.cpp b/src/ui/controls/IconButton.cpp index e20e422f..ee6c9cac 100644 --- a/src/ui/controls/IconButton.cpp +++ b/src/ui/controls/IconButton.cpp @@ -22,8 +22,6 @@ IconButton::IconButton(std::string_view icon_svg_path_data_string, SetIconWithSvgPathDataString(icon_svg_path_data_string, view_port); } -IconButton::~IconButton() {} - void IconButton::SetIconFillColor(const Color& color) { SetIconFillBrush(platform::gui::IUiApplication::GetInstance() ->GetGraphicsFactory() diff --git a/src/ui/controls/TextHostControlService.cpp b/src/ui/controls/TextHostControlService.cpp index 9c57140c..9142bd69 100644 --- a/src/ui/controls/TextHostControlService.cpp +++ b/src/ui/controls/TextHostControlService.cpp @@ -695,7 +695,7 @@ void TextHostControlService::OpenContextMenu(const Point& position, ContextMenuItem items) { CruLogDebug(kLogTag, "Open context menu."); if (!context_menu_) { - context_menu_.reset(new components::PopupMenu()); + context_menu_.reset(new components::PopupMenu(control_)); } auto menu = context_menu_->GetMenu(); menu->ClearItems(); diff --git a/src/ui/controls/Window.cpp b/src/ui/controls/Window.cpp index 8f06013e..f4714efa 100644 --- a/src/ui/controls/Window.cpp +++ b/src/ui/controls/Window.cpp @@ -2,6 +2,7 @@ #include "cru/platform/gui/UiApplication.h" #include "cru/platform/gui/Window.h" #include "cru/ui/Base.h" +#include "cru/ui/controls/Control.h" #include "cru/ui/controls/ControlHost.h" #include <cassert> @@ -15,16 +16,33 @@ Window::Window() GetContainerRenderObject()->SetDefaultVerticalAlignment(Alignment::Stretch); } -Window* Window::CreatePopup() { +Window* Window::CreatePopup(Control* attached_control) { auto window = new Window(); window->GetNativeWindow()->SetStyleFlag( platform::gui::WindowStyleFlags::NoCaptionAndBorder); + window->SetAttachedControl(attached_control); window->SetGainFocusOnCreateAndDestroyWhenLoseFocus(true); return window; } +Control* Window::GetAttachedControl() { return attached_control_; } + void Window::SetAttachedControl(Control* control) { attached_control_ = control; + if (control) { + if (auto control_host = control->GetControlHost()) { + control_host_->GetNativeWindow()->SetParent( + control_host->GetNativeWindow()); + } + parent_window_guard_.Reset(control->ControlHostChangeEvent()->AddHandler( + [this](const ControlHostChangeEventArgs& args) { + control_host_->GetNativeWindow()->SetParent( + args.new_host ? args.new_host->GetNativeWindow() : nullptr); + })); + } else { + control_host_->GetNativeWindow()->SetParent(nullptr); + parent_window_guard_.Reset(); + } } platform::gui::INativeWindow* Window::GetNativeWindow() { |
