diff options
| -rw-r--r-- | include/cru/platform/gui/win/Base.h | 2 | ||||
| -rw-r--r-- | include/cru/platform/gui/win/GodWindow.h | 34 | ||||
| -rw-r--r-- | include/cru/platform/gui/win/UiApplication.h | 7 | ||||
| -rw-r--r-- | include/cru/ui/DeleteLater.h | 3 | ||||
| -rw-r--r-- | include/cru/ui/controls/TextHostControlService.h | 5 | ||||
| -rw-r--r-- | include/cru/ui/events/UiEvents.h | 16 | ||||
| -rw-r--r-- | include/cru/ui/host/WindowHost.h | 1 | ||||
| -rw-r--r-- | src/platform/gui/win/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/platform/gui/win/Clipboard.cpp | 8 | ||||
| -rw-r--r-- | src/platform/gui/win/GodWindow.cpp | 61 | ||||
| -rw-r--r-- | src/platform/gui/win/TimerManager.cpp | 95 | ||||
| -rw-r--r-- | src/platform/gui/win/TimerManager.h | 53 | ||||
| -rw-r--r-- | src/platform/gui/win/UiApplication.cpp | 47 | ||||
| -rw-r--r-- | src/platform/gui/win/Window.cpp | 3 | ||||
| -rw-r--r-- | src/ui/DeleteLater.cpp | 7 | ||||
| -rw-r--r-- | src/ui/controls/Control.cpp | 5 | ||||
| -rw-r--r-- | src/ui/controls/TextHostControlService.cpp | 6 | ||||
| -rw-r--r-- | test/base/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | test/base/EventTest.cpp | 39 | ||||
| -rw-r--r-- | test/ui/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | test/ui/DeleteLaterTest.cpp | 17 |
21 files changed, 120 insertions, 298 deletions
diff --git a/include/cru/platform/gui/win/Base.h b/include/cru/platform/gui/win/Base.h index 9c253950..1ef20ffd 100644 --- a/include/cru/platform/gui/win/Base.h +++ b/include/cru/platform/gui/win/Base.h @@ -17,8 +17,6 @@ namespace cru::platform::gui::win { using platform::win::HResultError; using platform::win::Win32Error; -class GodWindow; -class TimerManager; class WinCursor; class WinCursorManager; class WindowClass; diff --git a/include/cru/platform/gui/win/GodWindow.h b/include/cru/platform/gui/win/GodWindow.h deleted file mode 100644 index 22068ff5..00000000 --- a/include/cru/platform/gui/win/GodWindow.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "Base.h" -#include "WindowNativeMessageEventArgs.h" - -#include <cru/base/Event.h> - -#include <memory> - -namespace cru::platform::gui::win { -class CRU_WIN_GUI_API GodWindow : public Object { - CRU_DEFINE_CLASS_LOG_TAG("GodWindow") - - public: - explicit GodWindow(WinUiApplication* application); - ~GodWindow() override; - - HWND GetHandle() const { return hwnd_; } - - bool HandleGodWindowMessage(HWND hwnd, UINT msg, WPARAM w_param, - LPARAM l_param, LRESULT* result); - - IEvent<WindowNativeMessageEventArgs&>* MessageEvent() { - return &message_event_; - } - - private: - WinUiApplication* application_; - - std::unique_ptr<WindowClass> god_window_class_; - HWND hwnd_; - - Event<WindowNativeMessageEventArgs&> message_event_; -}; -} // namespace cru::platform::gui::win diff --git a/include/cru/platform/gui/win/UiApplication.h b/include/cru/platform/gui/win/UiApplication.h index 9b1d011b..15c23148 100644 --- a/include/cru/platform/gui/win/UiApplication.h +++ b/include/cru/platform/gui/win/UiApplication.h @@ -2,6 +2,7 @@ #include "Base.h" #include <cru/platform/gui/UiApplication.h> +#include <cru/base/Timer.h> #include <memory> @@ -59,8 +60,6 @@ class CRU_WIN_GUI_API WinUiApplication : public WinNativeResource, HINSTANCE GetInstanceHandle() const { return instance_handle_; } - GodWindow* GetGodWindow() const { return god_window_.get(); } - TimerManager* GetTimerManager() const { return timer_manager_.get(); } WindowManager* GetWindowManager() const { return window_manager_.get(); } private: @@ -71,8 +70,8 @@ class CRU_WIN_GUI_API WinUiApplication : public WinNativeResource, std::unique_ptr<cru::platform::graphics::direct2d::DirectGraphicsFactory> graph_factory_; - std::unique_ptr<GodWindow> god_window_; - std::unique_ptr<TimerManager> timer_manager_; + TimerRegistry<std::function<void()>> timers_; + std::unique_ptr<WindowManager> window_manager_; std::unique_ptr<WinCursorManager> cursor_manager_; diff --git a/include/cru/ui/DeleteLater.h b/include/cru/ui/DeleteLater.h index d04d1814..ddbf2ce1 100644 --- a/include/cru/ui/DeleteLater.h +++ b/include/cru/ui/DeleteLater.h @@ -7,6 +7,7 @@ namespace cru::ui { class CRU_UI_API DeleteLaterImpl { + CRU_DEFINE_CLASS_LOG_TAG("cru::ui::DeleteLaterImpl") public: DeleteLaterImpl(); virtual ~DeleteLaterImpl(); @@ -36,7 +37,7 @@ DeleteLaterPtr<T> ToDeleteLaterPtr(std::unique_ptr<T>&& p) { template <typename T, typename... Args> DeleteLaterPtr<T> MakeDeleteLater(Args&&... args) { - return DeleteLaterPtr<T>(std::forward<Args>(args)...); + return DeleteLaterPtr<T>(new T(std::forward<Args>(args)...)); } } // namespace cru::ui diff --git a/include/cru/ui/controls/TextHostControlService.h b/include/cru/ui/controls/TextHostControlService.h index d4a1218f..e1586e66 100644 --- a/include/cru/ui/controls/TextHostControlService.h +++ b/include/cru/ui/controls/TextHostControlService.h @@ -2,7 +2,6 @@ #include "../render/TextRenderObject.h" #include "cru/platform/gui/InputMethod.h" #include "cru/platform/gui/UiApplication.h" -#include "cru/ui/DeleteLater.h" #include "cru/ui/controls/Control.h" #include "cru/ui/helper/ShortcutHub.h" @@ -71,7 +70,7 @@ class TextControlMovePattern { }; class CRU_UI_API TextHostControlService : public Object { - CRU_DEFINE_CLASS_LOG_TAG("TextControlService") + CRU_DEFINE_CLASS_LOG_TAG("cru::ui::controls::TextControlService") public: TextHostControlService(Control* control); @@ -207,6 +206,6 @@ class CRU_UI_API TextHostControlService : public Object { bool mouse_move_selecting_ = false; bool context_menu_dirty_ = true; - DeleteLaterPtr<components::PopupMenu> context_menu_; + std::unique_ptr<components::PopupMenu> context_menu_; }; } // namespace cru::ui::controls diff --git a/include/cru/ui/events/UiEvents.h b/include/cru/ui/events/UiEvents.h index 301ec694..a74c9d7d 100644 --- a/include/cru/ui/events/UiEvents.h +++ b/include/cru/ui/events/UiEvents.h @@ -1,10 +1,10 @@ #pragma once -#include "FocusChangeEventArgs.h" -#include "KeyEventArgs.h" -#include "MouseButtonEventArgs.h" -#include "MouseEventArgs.h" -#include "MouseWheelEventArgs.h" -#include "PaintEventArgs.h" -#include "RoutedEvent.h" -#include "UiEventArgs.h" +#include "FocusChangeEventArgs.h" // IWYU pragma: export +#include "KeyEventArgs.h" // IWYU pragma: export +#include "MouseButtonEventArgs.h" // IWYU pragma: export +#include "MouseEventArgs.h" // IWYU pragma: export +#include "MouseWheelEventArgs.h" // IWYU pragma: export +#include "PaintEventArgs.h" // IWYU pragma: export +#include "RoutedEvent.h" // IWYU pragma: export +#include "UiEventArgs.h" // IWYU pragma: export diff --git a/include/cru/ui/host/WindowHost.h b/include/cru/ui/host/WindowHost.h index 889665d5..58fd120d 100644 --- a/include/cru/ui/host/WindowHost.h +++ b/include/cru/ui/host/WindowHost.h @@ -9,7 +9,6 @@ #include <functional> #include <memory> -#include <optional> namespace cru::ui::host { class LayoutPaintCycler; diff --git a/src/platform/gui/win/CMakeLists.txt b/src/platform/gui/win/CMakeLists.txt index d5ac6851..8c0dd2a4 100644 --- a/src/platform/gui/win/CMakeLists.txt +++ b/src/platform/gui/win/CMakeLists.txt @@ -2,10 +2,8 @@ add_library(CruPlatformGuiWin Base.cpp Clipboard.cpp Cursor.cpp - GodWindow.cpp InputMethod.cpp Keyboard.cpp - TimerManager.cpp UiApplication.cpp Window.cpp WindowClass.cpp diff --git a/src/platform/gui/win/Clipboard.cpp b/src/platform/gui/win/Clipboard.cpp index f5d2513a..7062f160 100644 --- a/src/platform/gui/win/Clipboard.cpp +++ b/src/platform/gui/win/Clipboard.cpp @@ -1,7 +1,6 @@ #include "cru/platform/gui/win/Clipboard.h" #include "cru/base/StringUtil.h" #include "cru/base/log/Logger.h" -#include "cru/platform/gui/win/GodWindow.h" #include "cru/platform/gui/win/UiApplication.h" namespace cru::platform::gui::win { @@ -11,9 +10,7 @@ WinClipboard::WinClipboard(WinUiApplication* application) WinClipboard::~WinClipboard() {} std::string WinClipboard::GetText() { - auto god_window = application_->GetGodWindow(); - - if (!::OpenClipboard(god_window->GetHandle())) { + if (!::OpenClipboard(nullptr)) { CRU_LOG_TAG_WARN("Failed to open clipboard."); return {}; } @@ -47,9 +44,8 @@ std::string WinClipboard::GetText() { void WinClipboard::SetText(std::string utf8_text) { auto text = string::ToUtf16(utf8_text); - auto god_window = application_->GetGodWindow(); - if (!::OpenClipboard(god_window->GetHandle())) { + if (!::OpenClipboard(nullptr)) { CRU_LOG_TAG_WARN("Failed to open clipboard."); return; } diff --git a/src/platform/gui/win/GodWindow.cpp b/src/platform/gui/win/GodWindow.cpp deleted file mode 100644 index 7cdf229c..00000000 --- a/src/platform/gui/win/GodWindow.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "cru/platform/gui/win/GodWindow.h" -#include "cru/base/log/Logger.h" -#include "cru/platform/gui/win/UiApplication.h" -#include "cru/platform/gui/win/WindowClass.h" - -namespace cru::platform::gui::win { -constexpr auto god_window_class_name = L"GodWindowClass"; - -LRESULT CALLBACK GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, - LPARAM lParam) { - const auto app = WinUiApplication::GetInstance(); - - if (app) { - LRESULT result; - auto god_window = app->GetGodWindow(); - if (god_window != nullptr) { - const auto handled = god_window->HandleGodWindowMessage( - hWnd, uMsg, wParam, lParam, &result); - if (handled) return result; - } - } - return DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - -GodWindow::GodWindow(WinUiApplication* application) { - application_ = application; - - const auto h_instance = application->GetInstanceHandle(); - - god_window_class_ = std::make_unique<WindowClass>(god_window_class_name, - GodWndProc, h_instance); - - hwnd_ = CreateWindowEx(0, god_window_class_name, L"", 0, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - HWND_MESSAGE, nullptr, h_instance, nullptr); - - if (hwnd_ == nullptr) - throw Win32Error(::GetLastError(), "Failed to create god window."); -} - -GodWindow::~GodWindow() { - if (!::DestroyWindow(hwnd_)) { - // Although this could be "safely" ignore. - CRU_LOG_TAG_WARN("Failed to destroy god window."); - } -} - -bool GodWindow::HandleGodWindowMessage(HWND hwnd, UINT msg, WPARAM w_param, - LPARAM l_param, LRESULT* result) { - WindowNativeMessageEventArgs args( - WindowNativeMessage{hwnd, msg, w_param, l_param}); - message_event_.Raise(args); - - if (args.IsHandled()) { - *result = args.GetResult(); - return true; - } - - return false; -} -} // namespace cru::platform::gui::win diff --git a/src/platform/gui/win/TimerManager.cpp b/src/platform/gui/win/TimerManager.cpp deleted file mode 100644 index 794e714c..00000000 --- a/src/platform/gui/win/TimerManager.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "TimerManager.h" - -#include <functional> - -namespace cru::platform::gui::win { -constexpr int kSetImmediateWindowMessageId = WM_USER + 2000; - -TimerManager::TimerManager(GodWindow* god_window) { - god_window_ = god_window; - event_guard_ += god_window->MessageEvent()->AddHandler(std::bind( - &TimerManager::HandleGodWindowMessage, this, std::placeholders::_1)); -} - -long long TimerManager::SetTimer(TimerType type, int period, - std::function<void()> action) { - auto id = next_id_++; - TimerInfo timer_info{id, type, type == TimerType::Immediate ? 0 : period, - std::move(action)}; - if (type == TimerType::Immediate) { - if (!::PostMessageW(god_window_->GetHandle(), kSetImmediateWindowMessageId, - static_cast<UINT_PTR>(id), 0)) { - throw Win32Error( - ::GetLastError(), - "Failed to post window message to god window for set immediate."); - } - } else { - CreateNativeTimer(&timer_info); - } - - info_map_.emplace(id, std::move(timer_info)); - return id; -} - -void TimerManager::CancelTimer(long long id) { - if (id <= 0) return; - auto find_result = this->info_map_.find(id); - if (find_result != info_map_.cend()) { - auto& info = find_result->second; - KillNativeTimer(&info); - this->info_map_.erase(find_result); - } -} - -void TimerManager::CreateNativeTimer(TimerInfo* info) { - info->native_timer_id = static_cast<UINT_PTR>(info->id); - ::SetTimer(god_window_->GetHandle(), info->native_timer_id, info->period, - nullptr); -} - -void TimerManager::KillNativeTimer(TimerInfo* info) { - if (info->native_timer_id == 0) return; - ::KillTimer(god_window_->GetHandle(), info->native_timer_id); - info->native_timer_id = 0; -} - -void TimerManager::HandleGodWindowMessage(WindowNativeMessageEventArgs& args) { - const auto& message = args.GetWindowMessage(); - - switch (message.msg) { - case kSetImmediateWindowMessageId: { - auto find_result = - this->info_map_.find(static_cast<long long>(message.w_param)); - if (find_result != info_map_.cend()) { - auto& info = find_result->second; - info.action(); - info_map_.erase(find_result); - } - args.SetResult(0); - args.SetHandled(true); - return; - } - case WM_TIMER: { - auto find_result = - this->info_map_.find(static_cast<long long>(message.w_param)); - if (find_result != info_map_.cend()) { - auto& info = find_result->second; - if (info.type == TimerType::Interval) { - info.action(); - args.SetResult(0); - args.SetHandled(true); - } else if (info.type == TimerType::Timeout) { - info.action(); - KillNativeTimer(&info); - info_map_.erase(find_result); - args.SetResult(0); - args.SetHandled(true); - } - } - return; - } - default: - return; - } -} -} // namespace cru::platform::gui::win diff --git a/src/platform/gui/win/TimerManager.h b/src/platform/gui/win/TimerManager.h deleted file mode 100644 index b35ef1fc..00000000 --- a/src/platform/gui/win/TimerManager.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include "cru/base/Base.h" -#include "cru/base/Event.h" -#include "cru/platform/gui/win/GodWindow.h" -#include "cru/platform/gui/win/WindowNativeMessageEventArgs.h" - -#include <functional> -#include <unordered_map> - -namespace cru::platform::gui::win { -enum class TimerType { Immediate, Timeout, Interval }; - -struct TimerInfo { - TimerInfo(long long id, TimerType type, int period, - std::function<void()> action, UINT_PTR native_timer_id = 0) - : id(id), - type(type), - period(period), - action(std::move(action)), - native_timer_id(native_timer_id) {} - - long long id; - TimerType type; - int period; // in milliseconds - std::function<void()> action; - UINT_PTR native_timer_id; -}; - -class TimerManager : public Object { - public: - TimerManager(GodWindow* god_window); - ~TimerManager() override = default; - - // Period is in milliseconds. When type is immediate, it is not checked and - // used. - long long SetTimer(TimerType type, int period, std::function<void()> action); - void CancelTimer(long long id); - - private: - void HandleGodWindowMessage(WindowNativeMessageEventArgs& args); - - void CreateNativeTimer(TimerInfo* info); - void KillNativeTimer(TimerInfo* info); - - private: - GodWindow* god_window_; - - EventHandlerRevokerListGuard event_guard_; - - long long next_id_ = 1; - std::unordered_map<long long, TimerInfo> info_map_; -}; -} // namespace cru::platform::gui::win diff --git a/src/platform/gui/win/UiApplication.cpp b/src/platform/gui/win/UiApplication.cpp index 0de79675..12a74bc6 100644 --- a/src/platform/gui/win/UiApplication.cpp +++ b/src/platform/gui/win/UiApplication.cpp @@ -1,14 +1,14 @@ #include "cru/platform/gui/win/UiApplication.h" -#include "TimerManager.h" #include "WindowManager.h" #include "cru/platform/graphics/direct2d/Factory.h" #include "cru/platform/gui/win/Base.h" #include "cru/platform/gui/win/Clipboard.h" #include "cru/platform/gui/win/Cursor.h" -#include "cru/platform/gui/win/GodWindow.h" #include "cru/platform/gui/win/Window.h" +#include <chrono> + namespace cru::platform::gui { std::unique_ptr<IUiApplication> CreateUiApplication() { return std::make_unique<win::WinUiApplication>(); @@ -30,8 +30,6 @@ WinUiApplication::WinUiApplication() { graph_factory_ = std::make_unique< cru::platform::graphics::direct2d::DirectGraphicsFactory>(); - god_window_ = std::make_unique<GodWindow>(this); - timer_manager_ = std::make_unique<TimerManager>(god_window_.get()); window_manager_ = std::make_unique<WindowManager>(this); cursor_manager_ = std::make_unique<WinCursorManager>(); clipboard_ = std::make_unique<WinClipboard>(this); @@ -41,9 +39,28 @@ WinUiApplication::~WinUiApplication() { instance = nullptr; } int WinUiApplication::Run() { MSG msg; - while (GetMessageW(&msg, nullptr, 0, 0)) { - TranslateMessage(&msg); - DispatchMessageW(&msg); + bool exit = false; + + while (!exit) { + if (auto result = timers_.Update(std::chrono::steady_clock::now())) { + result->data(); + continue; + } + + auto timeout = timers_.NextTimeout(std::chrono::steady_clock::now()); + + ::MsgWaitForMultipleObjects( + 0, nullptr, FALSE, timeout ? timeout->count() : INFINITE, QS_ALLINPUT); + + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + + if (msg.message == WM_QUIT) { + exit = true; + break; + } + } } for (const auto& handler : quit_handlers_) handler(); @@ -60,27 +77,21 @@ void WinUiApplication::AddOnQuitHandler(std::function<void()> handler) { } long long WinUiApplication::SetImmediate(std::function<void()> action) { - return this->timer_manager_->SetTimer(TimerType::Immediate, 0, - std::move(action)); + return timers_.Add(std::move(action), std::chrono::milliseconds::zero(), + false); } long long WinUiApplication::SetTimeout(std::chrono::milliseconds milliseconds, std::function<void()> action) { - return this->timer_manager_->SetTimer(TimerType::Timeout, - static_cast<int>(milliseconds.count()), - std::move(action)); + return timers_.Add(std::move(action), milliseconds, false); } long long WinUiApplication::SetInterval(std::chrono::milliseconds milliseconds, std::function<void()> action) { - return this->timer_manager_->SetTimer(TimerType::Interval, - static_cast<int>(milliseconds.count()), - std::move(action)); + return timers_.Add(std::move(action), milliseconds, true); } -void WinUiApplication::CancelTimer(long long id) { - timer_manager_->CancelTimer(id); -} +void WinUiApplication::CancelTimer(long long id) { timers_.Remove(id); } std::vector<INativeWindow*> WinUiApplication::GetAllWindow() { const auto&& windows = window_manager_->GetAllWindows(); diff --git a/src/platform/gui/win/Window.cpp b/src/platform/gui/win/Window.cpp index 2acd4c75..af308768 100644 --- a/src/platform/gui/win/Window.cpp +++ b/src/platform/gui/win/Window.cpp @@ -68,7 +68,7 @@ Rect CalcClientRectFromWindow(const Rect& rect, WindowStyleFlag style_flag, WinNativeWindow::WinNativeWindow(WinUiApplication* application) : application_(application) { - Expects(application); // application can't be null. + input_method_context_ = std::make_unique<WinInputMethodContext>(this); } WinNativeWindow::~WinNativeWindow() { Close(); } @@ -490,7 +490,6 @@ void WinNativeWindow::RecreateWindow() { application_->GetDirectFactory(), hwnd_); window_render_target_->SetDpi(dpi_, dpi_); - input_method_context_ = std::make_unique<WinInputMethodContext>(this); input_method_context_->DisableIME(); } diff --git a/src/ui/DeleteLater.cpp b/src/ui/DeleteLater.cpp index 499b9b34..27ec2155 100644 --- a/src/ui/DeleteLater.cpp +++ b/src/ui/DeleteLater.cpp @@ -1,16 +1,21 @@ #include "cru/ui/DeleteLater.h" #include "Helper.h" +#include "cru/base/log/Logger.h" #include "cru/platform/gui/UiApplication.h" namespace cru::ui { DeleteLaterImpl::DeleteLaterImpl() : delete_later_scheduled_(false) {} -DeleteLaterImpl::~DeleteLaterImpl() {} +DeleteLaterImpl::~DeleteLaterImpl() { + CRU_LOG_TAG_DEBUG("Delete later object being deleted {}.", + static_cast<void*>(this)); +} void DeleteLaterImpl::DeleteLater() { if (!delete_later_scheduled_) { + CRU_LOG_TAG_DEBUG("Schedule delete later {}.", static_cast<void*>(this)); GetUiApplication()->SetImmediate([this] { delete this; }); delete_later_scheduled_ = true; } diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp index 3218f185..14397fa7 100644 --- a/src/ui/controls/Control.cpp +++ b/src/ui/controls/Control.cpp @@ -1,10 +1,8 @@ #include "cru/ui/controls/Control.h" -#include "cru/base/log/Logger.h" #include "cru/platform/gui/Cursor.h" #include "cru/platform/gui/UiApplication.h" #include "cru/ui/host/WindowHost.h" -#include "cru/ui/render/RenderObject.h" #include "cru/ui/style/StyleRuleSet.h" namespace cru::ui::controls { @@ -30,8 +28,7 @@ Control::Control() { Control::~Control() { if (host::WindowHost::IsInEventHandling()) { - CRU_LOG_TAG_ERROR( - "Control destroyed during event handling. Please use DeleteLater."); + std::terminate(); } in_destruction_ = true; diff --git a/src/ui/controls/TextHostControlService.cpp b/src/ui/controls/TextHostControlService.cpp index 235e6e6a..f581d3c9 100644 --- a/src/ui/controls/TextHostControlService.cpp +++ b/src/ui/controls/TextHostControlService.cpp @@ -12,7 +12,6 @@ #include "cru/platform/gui/Window.h" #include "cru/ui/Base.h" #include "cru/ui/DebugFlags.h" -#include "cru/ui/DeleteLater.h" #include "cru/ui/components/Menu.h" #include "cru/ui/helper/ShortcutHub.h" #include "cru/ui/host/WindowHost.h" @@ -149,7 +148,8 @@ std::vector<TextControlMovePattern> TextControlMovePattern::kDefaultPatterns = { TextHostControlService::TextHostControlService(Control* control) : control_(control), - text_host_control_(dynamic_cast<ITextHostControl*>(control)) { + text_host_control_(dynamic_cast<ITextHostControl*>(control)), + context_menu_(new components::PopupMenu()) { SetUpShortcuts(); SetupOneHandler(&Control::MouseMoveEvent, @@ -699,7 +699,7 @@ void TextHostControlService::SetUpShortcuts() { void TextHostControlService::OpenContextMenu(const Point& position, ContextMenuItem items) { - context_menu_ = MakeDeleteLater<components::PopupMenu>(); + CRU_LOG_TAG_DEBUG("Open context menu."); auto menu = context_menu_->GetMenu(); if (items & ContextMenuItem::kSelectAll) { menu->AddTextItem("Select All", [this] { this->SelectAll(); }); diff --git a/test/base/CMakeLists.txt b/test/base/CMakeLists.txt index 731f268c..52f61a8c 100644 --- a/test/base/CMakeLists.txt +++ b/test/base/CMakeLists.txt @@ -1,4 +1,5 @@ add_executable(CruBaseTest + EventTest.cpp PropertyTreeTest.cpp SelfResolvableTest.cpp StringUtilTest.cpp diff --git a/test/base/EventTest.cpp b/test/base/EventTest.cpp new file mode 100644 index 00000000..9ebf8b28 --- /dev/null +++ b/test/base/EventTest.cpp @@ -0,0 +1,39 @@ +#include "cru/base/Event.h" + +#include <catch2/catch_test_macros.hpp> + +TEST_CASE("Event should work.", "[event]") { + using cru::Event; + + Event<int&> event; + + int count = 0; + bool triggered = false; + + auto revoker = event.AddHandler([](int& count) { count++; }); + event.AddHandler([](int& count) { count++; }); + event.AddSpyOnlyHandler([&triggered] { triggered = true; }); + + event.Raise(count); + REQUIRE(count == 2); + REQUIRE(triggered); + + revoker(); + + event.Raise(count); + REQUIRE(count == 3); +} + +TEST_CASE("Event destroy during raising.", "[event]") { + using cru::Event; + + Event<int&>* event = new Event<int&>(); + + event->AddSpyOnlyHandler([event] { delete event; }); + auto revoker = event->AddHandler([](int& count) { count++; }); + + int count = 0; + event->Raise(count); + REQUIRE(count == 1); + revoker(); +} diff --git a/test/ui/CMakeLists.txt b/test/ui/CMakeLists.txt index e69de29b..66a7cd27 100644 --- a/test/ui/CMakeLists.txt +++ b/test/ui/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(CruUiTest + DeleteLaterTest.cpp +) +target_link_libraries(CruUiTest PRIVATE CruUi CruTestBase) + +cru_catch_discover_tests(CruUiTest) diff --git a/test/ui/DeleteLaterTest.cpp b/test/ui/DeleteLaterTest.cpp new file mode 100644 index 00000000..c9d600de --- /dev/null +++ b/test/ui/DeleteLaterTest.cpp @@ -0,0 +1,17 @@ +#include "cru/ui/DeleteLater.h" + +#include <catch2/catch_test_macros.hpp> + +struct MockDeleteLater { + bool triggered = false; + + void DeleteLater() { triggered = true; } +}; + +TEST_CASE("DeleteLaterPtr should work.", "[delete-later]") { + auto ptr = cru::ui::MakeDeleteLater<MockDeleteLater>(); + auto p = ptr.get(); + ptr.reset(); + REQUIRE(p->triggered); + delete p; +} |
