diff options
-rw-r--r-- | CruUI-Generate/CruUI-Generate.vcxproj | 130 | ||||
-rw-r--r-- | CruUI-Generate/CruUI-Generate.vcxproj.filters | 27 | ||||
-rw-r--r-- | CruUI-Generate/cru_ui.cpp | 4250 | ||||
-rw-r--r-- | CruUI-Generate/cru_ui.hpp | 3514 | ||||
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | scripts/merge.py | 13 | ||||
-rw-r--r-- | tools/cppmerge/.vscode/launch.json | 20 |
7 files changed, 1 insertions, 7963 deletions
diff --git a/CruUI-Generate/CruUI-Generate.vcxproj b/CruUI-Generate/CruUI-Generate.vcxproj deleted file mode 100644 index f97cef59..00000000 --- a/CruUI-Generate/CruUI-Generate.vcxproj +++ /dev/null @@ -1,130 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <VCProjectVersion>15.0</VCProjectVersion> - <ProjectGuid>{E823676A-8860-4177-B062-B003B7F66A6F}</ProjectGuid> - <RootNamespace>CruUIGenerate</RootNamespace> - <WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v141</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="Shared"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup /> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <ConformanceMode>true</ConformanceMode> - <LanguageStandard>stdcpplatest</LanguageStandard> - </ClCompile> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <SDLCheck>true</SDLCheck> - <ConformanceMode>true</ConformanceMode> - <LanguageStandard>stdcpplatest</LanguageStandard> - </ClCompile> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <ConformanceMode>true</ConformanceMode> - <LanguageStandard>stdcpplatest</LanguageStandard> - </ClCompile> - <Link> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <ConformanceMode>true</ConformanceMode> - <LanguageStandard>stdcpplatest</LanguageStandard> - </ClCompile> - <Link> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="cru_ui.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="cru_ui.hpp" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project>
\ No newline at end of file diff --git a/CruUI-Generate/CruUI-Generate.vcxproj.filters b/CruUI-Generate/CruUI-Generate.vcxproj.filters deleted file mode 100644 index af138685..00000000 --- a/CruUI-Generate/CruUI-Generate.vcxproj.filters +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - </ItemGroup> - <ItemGroup> - <ClCompile Include="cru_ui.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ClInclude Include="cru_ui.hpp"> - <Filter>Header Files</Filter> - </ClInclude> - </ItemGroup> -</Project>
\ No newline at end of file diff --git a/CruUI-Generate/cru_ui.cpp b/CruUI-Generate/cru_ui.cpp deleted file mode 100644 index 9a4e335d..00000000 --- a/CruUI-Generate/cru_ui.cpp +++ /dev/null @@ -1,4250 +0,0 @@ -#include "cru_ui.hpp" -//-------------------------------------------------------- -//-------begin of file: src\any_map.cpp -//-------------------------------------------------------- - -namespace cru -{ - AnyMap::ListenerToken AnyMap::RegisterValueChangeListener(const String& key, const Listener& listener) - { - const auto token = current_listener_token_++; - map_[key].second.push_back(token); - listeners_.emplace(token, listener); - return token; - } - - void AnyMap::UnregisterValueChangeListener(const ListenerToken token) - { - const auto find_result = listeners_.find(token); - if (find_result != listeners_.cend()) - listeners_.erase(find_result); - } - - void AnyMap::InvokeListeners(std::list<ListenerToken>& listener_list, const std::any& value) - { - auto i = listener_list.cbegin(); - while (i != listener_list.cend()) - { - auto current_i = i++; - const auto find_result = listeners_.find(*current_i); - if (find_result != listeners_.cend()) - find_result->second(value); - else - listener_list.erase(current_i); // otherwise remove the invalid listener token. - } - } -} -//-------------------------------------------------------- -//-------end of file: src\any_map.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\application.cpp -//-------------------------------------------------------- - - -namespace cru { - constexpr auto god_window_class_name = L"GodWindowClass"; - constexpr int invoke_later_message_id = WM_USER + 2000; - - - LRESULT CALLBACK GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - const auto app = Application::GetInstance(); - - if (app) - { - const auto result = app->GetGodWindow()->HandleGodWindowMessage(hWnd, uMsg, wParam, lParam); - if (result.has_value()) - return result.value(); - else - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } - else - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } - - GodWindow::GodWindow(Application* application) - { - const auto h_instance = application->GetInstanceHandle(); - - god_window_class_ = std::make_unique<ui::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 std::runtime_error("Failed to create window."); - } - - GodWindow::~GodWindow() - { - ::DestroyWindow(hwnd_); - } - - std::optional<LRESULT> GodWindow::HandleGodWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param) - { - switch (msg) - { - case invoke_later_message_id: - { - const auto p_action = reinterpret_cast<std::function<void()>*>(w_param); - (*p_action)(); - delete p_action; - return 0; - } - case WM_TIMER: - { - const auto id = static_cast<UINT_PTR>(w_param); - const auto action = TimerManager::GetInstance()->GetAction(id); - if (action.has_value()) - { - (action.value().second)(); - if (!action.value().first) - TimerManager::GetInstance()->KillTimer(id); - return 0; - } - break; - } - default: - return std::nullopt; - } - return std::nullopt; - } - - - - Application* Application::instance_ = nullptr; - - Application * Application::GetInstance() { - return instance_; - } - - Application::Application(HINSTANCE h_instance) - : h_instance_(h_instance) { - - if (instance_) - throw std::runtime_error("A application instance already exists."); - - instance_ = this; - - if (!::IsWindows8OrGreater()) - throw std::runtime_error("Must run on Windows 8 or later."); - - god_window_ = std::make_unique<GodWindow>(this); - - ui::cursors::LoadSystemCursors(); - } - - Application::~Application() - { - for (auto i = singleton_list_.crbegin(); i != singleton_list_.crend(); ++i) - delete *i; - instance_ = nullptr; - } - - int Application::Run() - { - MSG msg; - - while (GetMessage(&msg, nullptr, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - return static_cast<int>(msg.wParam); - } - - void Application::Quit(const int quit_code) { - ::PostQuitMessage(quit_code); - } - - void InvokeLater(const std::function<void()>& action) { - //copy the action to a safe place - auto p_action_copy = new std::function<void()>(action); - - if (PostMessageW(Application::GetInstance()->GetGodWindow()->GetHandle(), invoke_later_message_id, reinterpret_cast<WPARAM>(p_action_copy), 0) == 0) - throw Win32Error(::GetLastError(), "InvokeLater failed to post message."); - } -} -//-------------------------------------------------------- -//-------end of file: src\application.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\base.cpp -//-------------------------------------------------------- - - -namespace cru -{ - MultiByteString ToUtf8String(const StringView& string) - { - if (string.empty()) - return MultiByteString(); - - const auto length = ::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, nullptr, 0, nullptr, nullptr); - MultiByteString result; - result.reserve(length); - if (::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, result.data(), static_cast<int>(result.capacity()), nullptr, nullptr) == 0) - throw Win32Error(::GetLastError(), "Failed to convert wide string to UTF-8."); - return result; - } -} -//-------------------------------------------------------- -//-------end of file: src\base.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\cru_debug.cpp -//-------------------------------------------------------- - - -namespace cru::debug -{ - void DebugMessage(const StringView& message) - { - ::OutputDebugStringW(message.data()); - } -} -//-------------------------------------------------------- -//-------end of file: src\cru_debug.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\exception.cpp -//-------------------------------------------------------- - - -namespace cru -{ - inline std::string HResultMakeMessage(HRESULT h_result, std::optional<MultiByteStringView> message) - { - char buffer[10]; - sprintf_s(buffer, "%#08x", h_result); - - if (message.has_value()) - return Format("An HResultError is thrown. HRESULT: {}.\nAdditional message: {}\n", buffer, message.value()); - else - return Format("An HResultError is thrown. HRESULT: {}.\n", buffer); - } - - HResultError::HResultError(HRESULT h_result, std::optional<MultiByteStringView> additional_message) - : runtime_error(HResultMakeMessage(h_result, std::nullopt)), h_result_(h_result) - { - - } - - inline std::string Win32MakeMessage(DWORD error_code, std::optional<MultiByteStringView> message) - { - char buffer[10]; - sprintf_s(buffer, "%#04x", error_code); - - if (message.has_value()) - return Format("Last error code: {}.\nAdditional message: {}\n", buffer, message.value()); - else - return Format("Last error code: {}.\n", buffer); - } - - Win32Error::Win32Error(DWORD error_code, std::optional<MultiByteStringView> additional_message) - : runtime_error(Win32MakeMessage(error_code, std::nullopt)), error_code_(error_code) - { - - } -} -//-------------------------------------------------------- -//-------end of file: src\exception.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\main.cpp -//-------------------------------------------------------- - - -using cru::String; -using cru::StringView; -using cru::Application; -using cru::ui::Rect; -using cru::ui::Window; -using cru::ui::Alignment; -using cru::ui::LayoutSideParams; -using cru::ui::Thickness; -using cru::ui::ControlList; -using cru::ui::CreateWithLayout; -using cru::ui::controls::LinearLayout; -using cru::ui::controls::TextBlock; -using cru::ui::controls::ToggleButton; -using cru::ui::controls::Button; -using cru::ui::controls::TextBox; -using cru::ui::controls::ListItem; -using cru::ui::controls::FrameLayout; -using cru::ui::controls::ScrollControl; - -int APIENTRY wWinMain( - HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPWSTR lpCmdLine, - int nCmdShow) { - -#ifdef CRU_DEBUG - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); -#endif - - Application application(hInstance); - - const auto window = Window::CreateOverlapped(); - /* - window.native_message_event.AddHandler([](cru::ui::events::WindowNativeMessageEventArgs& args) - { - if (args.GetWindowMessage().msg == WM_PAINT) - { - OutputDebugStringW(L"Paint!\n"); - //args.SetResult(0); - } - }); - */ - /* - // test1 - cru::ui::controls::TextBlock text_block; - text_block.SetText(L"Hello world!"); - text_block.SetSize(cru::ui::Size(200, 30)); - window.AddChild(&text_block); - - std::array<D2D_COLOR_F, 4> colors = - { - D2D1::ColorF(D2D1::ColorF::Blue), - D2D1::ColorF(D2D1::ColorF::Yellow), - D2D1::ColorF(D2D1::ColorF::Green), - D2D1::ColorF(D2D1::ColorF::Red) - }; - - std::random_device rd; // only used once to initialise (seed) engine - std::mt19937 rng(rd()); // random-number engine used (Mersenne-Twister in this case) - std::uniform_int_distribution<decltype(colors.size())> uni(0, colors.size() - 1); // guaranteed unbiased - - - window.draw_event.AddHandler([&](cru::ui::events::DrawEventArgs& args) { - auto device_context = args.GetDeviceContext(); - - ID2D1SolidColorBrush* brush; - device_context->CreateSolidColorBrush(colors[uni(rng)], &brush); - - device_context->FillRectangle(D2D1::RectF(100.0f, 100.0f, 300.0f, 200.0f), brush); - - brush->Release(); - }); - - cru::SetTimeout(2.0, [&window]() { - window.InvalidateDraw(); - - auto task = cru::SetInterval(0.5, [&window]() { - window.InvalidateDraw(); - }); - - cru::SetTimeout(4, [task]() { - task->Cancel(); - task->Cancel(); // test for idempotency. - }); - }); - */ - - - //test 2 - const auto layout = CreateWithLayout<LinearLayout>(LayoutSideParams::Exactly(500), LayoutSideParams::Content()); - - layout->mouse_click_event.bubble.AddHandler([layout](cru::ui::events::MouseButtonEventArgs& args) - { - if (args.GetSender() == args.GetOriginalSender()) - layout->AddChild(TextBlock::Create(L"Layout is clicked!")); - }); - - { - const auto inner_layout = CreateWithLayout<LinearLayout>(LayoutSideParams::Content(Alignment::End), LayoutSideParams::Content(), LinearLayout::Orientation::Horizontal); - - inner_layout->AddChild(TextBlock::Create(L"Toggle debug border")); - - const auto l = FrameLayout::Create(); - l->GetLayoutParams()->padding.SetLeftRight(20.0f); - const auto toggle_button = ToggleButton::Create(); -#ifdef CRU_DEBUG_LAYOUT - toggle_button->toggle_event.AddHandler([&window](cru::ui::events::ToggleEventArgs& args) - { - window->SetDebugLayout(args.GetNewState()); - }); -#endif - l->AddChild(toggle_button); - inner_layout->AddChild(l); - layout->AddChild(inner_layout); - } - - { - const auto button = Button::Create(); - button->GetLayoutParams()->padding = Thickness(20, 5); - button->SetChild(TextBlock::Create(L"Show popup window parenting this.")); - button->mouse_click_event.bubble.AddHandler([window, button](auto) - { - std::vector<cru::ui::controls::MenuItemInfo> items; - items.emplace_back(L"Hello world!", []{}); - items.emplace_back(L"Item 2", []{}); - items.emplace_back(L"Close parent window.", [window]{ window->Close(); }); - - cru::ui::controls::CreatePopupMenu(window->PointToScreen(button->GetPositionAbsolute()), items, window)->Show(); - }); - layout->AddChild(button); - } - - { - const auto button = Button::Create(); - button->GetLayoutParams()->padding = Thickness(20, 5); - button->SetChild(TextBlock::Create(L"Show popup window parenting null.")); - button->SetBackgroundBrush(cru::graph::CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gold))); - button->mouse_click_event.bubble.AddHandler([](auto) - { - auto popup = Window::CreatePopup(nullptr); - popup->SetWindowRect(Rect(100, 100, 300, 300)); - popup->Show(); - }); - layout->AddChild(button); - } - - { - const auto button = Button::Create(); - button->GetLayoutParams()->padding = Thickness(20, 5); - button->SetChild(TextBlock::Create(L"Show popup window with caption.")); - button->mouse_click_event.bubble.AddHandler([](auto) - { - auto popup = Window::CreatePopup(nullptr, true); - popup->SetWindowRect(Rect(100, 100, 300, 300)); - popup->Show(); - }); - layout->AddChild(button); - } - - { - const auto text_block = CreateWithLayout<TextBlock>(LayoutSideParams::Exactly(200), LayoutSideParams::Exactly(80), L"Hello World!!!"); - - text_block->mouse_click_event.bubble.AddHandler([layout](cru::ui::events::MouseButtonEventArgs& args) - { - layout->AddChild(TextBlock::Create(L"Hello world is clicked!")); - }); - - layout->AddChild(text_block); - } - - { - const auto text_box = TextBox::Create(); - text_box->GetLayoutParams()->width.min = 50.0f; - text_box->GetLayoutParams()->width.max = 100.0f; - text_box->char_event.tunnel.AddHandler([](cru::ui::events::CharEventArgs& args) - { - if (args.GetChar() == L'1') - args.SetHandled(); - }); - layout->AddChild(text_box); - } - - { - const auto scroll_view = CreateWithLayout<ScrollControl>(LayoutSideParams::Stretch(), LayoutSideParams::Stretch()); - - scroll_view->SetVerticalScrollBarVisibility(ScrollControl::ScrollBarVisibility::Always); - - const auto text_block = TextBlock::Create( - L"Love myself I do. Not everything, but I love the good as well as the bad. I love my crazy lifestyle, and I love my hard discipline. I love my freedom of speech and the way my eyes get dark when I'm tired. I love that I have learned to trust people with my heart, even if it will get broken. I am proud of everything that I am and will become."); - text_block->SetSelectable(true); - - scroll_view->SetChild(text_block); - layout->AddChild(scroll_view); - } - - layout->AddChild(CreateWithLayout<TextBlock>(LayoutSideParams::Content(Alignment::Start), LayoutSideParams::Content(), L"This is a little short sentence!!!")); - layout->AddChild(CreateWithLayout<TextBlock>(LayoutSideParams::Content(Alignment::End), LayoutSideParams::Stretch(), L"By crupest!!!")); - - - window->SetChild(layout); - - /* - window.AddChild( - CreateWithLayout<Border>(LayoutSideParams::Exactly(200), LayoutSideParams::Content(), - std::initializer_list<cru::ui::Control*>{ - CreateWithLayout<TextBox>(LayoutSideParams::Stretch(), LayoutSideParams::Content()) - } - )); - */ - - /* test 3 - const auto linear_layout = CreateWithLayout<LinearLayout>(Thickness(50, 50), Thickness(50, 50), LinearLayout::Orientation::Vertical, ControlList{ - Button::Create({ - TextBlock::Create(L"Button") - }), - CreateWithLayout<TextBox>(Thickness(30), Thickness(20)) - }); - - linear_layout->SetBordered(true); - - window.AddChild(linear_layout); - */ - - window->Show(); - - return application.Run(); -} -//-------------------------------------------------------- -//-------end of file: src\main.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\timer.cpp -//-------------------------------------------------------- - - -namespace cru -{ - TimerManager* TimerManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton<TimerManager>([](auto) - { - return new TimerManager{}; - }); - } - - UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop, const TimerAction& action) - { - const auto id = current_count_++; - ::SetTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id, milliseconds, nullptr); - map_.emplace(id, std::make_pair(loop, action)); - return id; - } - - void TimerManager::KillTimer(const UINT_PTR id) - { - const auto find_result = map_.find(id); - if (find_result != map_.cend()) - { - ::KillTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id); - map_.erase(find_result); - } - } - - std::optional<std::pair<bool, TimerAction>> TimerManager::GetAction(const UINT_PTR id) - { - const auto find_result = map_.find(id); - if (find_result == map_.cend()) - return std::nullopt; - return find_result->second; - } - - TimerTask::TimerTask(const UINT_PTR id) - : id_(id) - { - - } - - void TimerTask::Cancel() const - { - TimerManager::GetInstance()->KillTimer(id_); - } - - TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action) - { - const auto id = TimerManager::GetInstance()->CreateTimer(static_cast<UINT>(milliseconds.count()), false, action); - return TimerTask(id); - } - - TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action) - { - const auto id = TimerManager::GetInstance()->CreateTimer(static_cast<UINT>(milliseconds.count()), true, action); - return TimerTask(id); - } -} -//-------------------------------------------------------- -//-------end of file: src\timer.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\graph\graph.cpp -//-------------------------------------------------------- - - -namespace cru::graph -{ - using Microsoft::WRL::ComPtr; - - WindowRenderTarget::WindowRenderTarget(GraphManager* graph_manager, HWND hwnd) - { - this->graph_manager_ = graph_manager; - - const auto d3d11_device = graph_manager->GetD3D11Device(); - const auto dxgi_factory = graph_manager->GetDxgiFactory(); - - // Allocate a descriptor. - DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = { 0 }; - swap_chain_desc.Width = 0; // use automatic sizing - swap_chain_desc.Height = 0; - swap_chain_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format - swap_chain_desc.Stereo = false; - swap_chain_desc.SampleDesc.Count = 1; // don't use multi-sampling - swap_chain_desc.SampleDesc.Quality = 0; - swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swap_chain_desc.BufferCount = 2; // use double buffering to enable flip - swap_chain_desc.Scaling = DXGI_SCALING_NONE; - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all apps must use this SwapEffect - swap_chain_desc.Flags = 0; - - - - // Get the final swap chain for this window from the DXGI factory. - ThrowIfFailed( - dxgi_factory->CreateSwapChainForHwnd( - d3d11_device.Get(), - hwnd, - &swap_chain_desc, - nullptr, - nullptr, - &dxgi_swap_chain_ - ) - ); - - CreateTargetBitmap(); - } - - WindowRenderTarget::~WindowRenderTarget() - { - - } - - void WindowRenderTarget::ResizeBuffer(const int width, const int height) - { - const auto graph_manager = graph_manager_; - const auto d2d1_device_context = graph_manager->GetD2D1DeviceContext(); - - ComPtr<ID2D1Image> old_target; - d2d1_device_context->GetTarget(&old_target); - const auto target_this = old_target == this->target_bitmap_; - if (target_this) - d2d1_device_context->SetTarget(nullptr); - - old_target = nullptr; - target_bitmap_ = nullptr; - - ThrowIfFailed( - dxgi_swap_chain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0) - ); - - CreateTargetBitmap(); - - if (target_this) - d2d1_device_context->SetTarget(target_bitmap_.Get()); - } - - void WindowRenderTarget::SetAsTarget() - { - GetD2DDeviceContext()->SetTarget(target_bitmap_.Get()); - } - - void WindowRenderTarget::Present() - { - ThrowIfFailed( - dxgi_swap_chain_->Present(1, 0) - ); - } - - void WindowRenderTarget::CreateTargetBitmap() - { - // Direct2D needs the dxgi version of the backbuffer surface pointer. - ComPtr<IDXGISurface> dxgiBackBuffer; - ThrowIfFailed( - dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer)) - ); - - const auto dpi = graph_manager_->GetDpi(); - - auto bitmap_properties = - D2D1::BitmapProperties1( - D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, - D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), - dpi.x, - dpi.y - ); - - // Get a D2D surface from the DXGI back buffer to use as the D2D render target. - ThrowIfFailed( - graph_manager_->GetD2D1DeviceContext()->CreateBitmapFromDxgiSurface( - dxgiBackBuffer.Get(), - &bitmap_properties, - &target_bitmap_ - ) - ); - } - - GraphManager* GraphManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton<GraphManager>([](auto) - { - return new GraphManager{}; - }); - } - - GraphManager::GraphManager() - { - UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - -#ifdef CRU_DEBUG - creation_flags |= D3D11_CREATE_DEVICE_DEBUG; -#endif - - const D3D_FEATURE_LEVEL feature_levels[] = - { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; - - - ThrowIfFailed(D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_HARDWARE, - nullptr, - creation_flags, - feature_levels, - ARRAYSIZE(feature_levels), - D3D11_SDK_VERSION, - &d3d11_device_, - nullptr, - &d3d11_device_context_ - )); - - Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device; - - ThrowIfFailed(d3d11_device_.As(&dxgi_device)); - - ThrowIfFailed(D2D1CreateFactory( - D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory1), - &d2d1_factory_ - )); - - ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); - - ThrowIfFailed(d2d1_device_->CreateDeviceContext( - D2D1_DEVICE_CONTEXT_OPTIONS_NONE, - &d2d1_device_context_ - )); - - // Identify the physical adapter (GPU or card) this device is runs on. - ComPtr<IDXGIAdapter> dxgi_adapter; - ThrowIfFailed( - dxgi_device->GetAdapter(&dxgi_adapter) - ); - - // Get the factory object that created the DXGI device. - ThrowIfFailed( - dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_)) - ); - - - ThrowIfFailed(DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown**>(dwrite_factory_.GetAddressOf()) - )); - - dwrite_factory_->GetSystemFontCollection(&dwrite_system_font_collection_); - } - - GraphManager::~GraphManager() - { - - } - - std::shared_ptr<WindowRenderTarget> GraphManager::CreateWindowRenderTarget(HWND hwnd) - { - return std::make_shared<WindowRenderTarget>(this, hwnd); - } - - Dpi GraphManager::GetDpi() const - { - Dpi dpi; - d2d1_factory_->GetDesktopDpi(&dpi.x, &dpi.y); - return dpi; - } - - void GraphManager::ReloadSystemMetrics() - { - ThrowIfFailed( - d2d1_factory_->ReloadSystemMetrics() - ); - } - - Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidColorBrush(const D2D1_COLOR_F& color) - { - Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush; - ThrowIfFailed(GraphManager::GetInstance()->GetD2D1DeviceContext()->CreateSolidColorBrush(color, &brush)); - return brush; - } -} -//-------------------------------------------------------- -//-------end of file: src\graph\graph.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\border_property.cpp -//-------------------------------------------------------- - - -namespace cru::ui -{ - BorderProperty::BorderProperty(): BorderProperty(UiManager::GetInstance()->GetPredefineResources()->border_property_brush) - { - - } - - BorderProperty::BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush): brush_(std::move(brush)) - { - - } - - BorderProperty::BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush, const float width, const float radius_x, - const float radius_y, Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style) : - brush_(std::move(brush)), stroke_width_(width), radius_x_(radius_x), radius_y_(radius_y), stroke_style_(std::move(stroke_style)) - { - - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\border_property.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\control.cpp -//-------------------------------------------------------- - -#include <algorithm> -#include <cassert> - - -#ifdef CRU_DEBUG_LAYOUT -#endif - -namespace cru::ui -{ - Control::Control() - { - mouse_leave_event.bubble.AddHandler([this](events::MouseEventArgs& args) - { - if (args.GetOriginalSender() != this) - return; - for (auto& is_mouse_click_valid : is_mouse_click_valid_map_) - { - if (is_mouse_click_valid.second) - { - is_mouse_click_valid.second = false; - OnMouseClickEnd(is_mouse_click_valid.first); - } - } - }); - - mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetOriginalSender() != this) - return; - - if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender()) - RequestFocus(); - const auto button = args.GetMouseButton(); - is_mouse_click_valid_map_[button] = true; - OnMouseClickBegin(button); - }); - - mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetOriginalSender() != this) - return; - - const auto button = args.GetMouseButton(); - if (is_mouse_click_valid_map_[button]) - { - is_mouse_click_valid_map_[button] = false; - OnMouseClickEnd(button); - const auto point = args.GetPoint(GetWindow()); - InvokeLater([this, button, point] - { - DispatchEvent(this, &Control::mouse_click_event, nullptr, point, button); - }); - } - }); - } - - - void Control::SetParent(Control* parent) - { - const auto old_parent = GetParent(); - parent_ = parent; - const auto new_parent = GetParent(); - if (old_parent != new_parent) - OnParentChanged(old_parent, new_parent); - } - - void Control::SetInternalParent(Control* internal_parent) - { - const auto old_internal_parent = GetInternalParent(); - const auto old_parent = GetParent(); - internal_parent_ = internal_parent; - const auto new_internal_parent = GetInternalParent(); - const auto new_parent = GetParent(); - if (old_parent != new_parent) - OnParentChanged(old_parent, new_parent); - if (old_internal_parent != new_internal_parent) - OnInternalParentChanged(old_internal_parent, new_internal_parent); - } - - void Control::SetDescendantWindow(Window* window) - { - if (window == nullptr && window_ == nullptr) - return; - - //You can only attach or detach window. - assert((window != nullptr && window_ == nullptr) || (window == nullptr && window_ != nullptr)); - - if (window == nullptr) - { - const auto old = window_; - TraverseDescendants([old](Control* control) - { - control->window_ = nullptr; - control->OnDetachToWindow(old); - }); - } - else - TraverseDescendants([window](Control* control) - { - control->window_ = window; - control->OnAttachToWindow(window); - }); - } - - - void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate) - { - predicate(control); - for (auto c: control->GetInternalChildren()) - TraverseDescendantsInternal(c, predicate); - } - - void Control::TraverseDescendants(const std::function<void(Control*)>& predicate) - { - TraverseDescendantsInternal(this, predicate); - } - - Point Control::GetOffset() - { - return rect_.GetLeftTop(); - } - - Size Control::GetSize() - { - return rect_.GetSize(); - } - - void Control::SetRect(const Rect& rect) - { - const auto old_rect = rect_; - rect_ = rect; - - RegenerateGeometryInfo(); - - OnRectChange(old_rect, rect); - - if (auto window = GetWindow()) - window->InvalidateDraw(); - } - - namespace - { -#ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr<ID2D1Geometry> CalculateSquareRingGeometry(const Rect& out, const Rect& in) - { - const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> out_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry)); - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> in_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry)); - Microsoft::WRL::ComPtr<ID2D1PathGeometry> result_geometry; - ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry)); - Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink; - ThrowIfFailed(result_geometry->Open(&sink)); - ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get())); - ThrowIfFailed(sink->Close()); - return result_geometry; - } -#endif - } - - Point Control::GetPositionAbsolute() const - { - return position_cache_.lefttop_position_absolute; - } - - Point Control::ControlToWindow(const Point& point) const - { - return Point(point.x + position_cache_.lefttop_position_absolute.x, - point.y + position_cache_.lefttop_position_absolute.y); - } - - Point Control::WindowToControl(const Point & point) const - { - return Point(point.x - position_cache_.lefttop_position_absolute.x, - point.y - position_cache_.lefttop_position_absolute.y); - } - - void Control::RefreshDescendantPositionCache() - { - auto point = Point::Zero(); - auto parent = this; - while ((parent = parent->GetParent())) - { - const auto p = parent->GetOffset(); - point.x += p.x; - point.y += p.y; - } - RefreshControlPositionCacheInternal(this, point); - } - - void Control::RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute) - { - const auto position = control->GetOffset(); - const Point lefttop( - parent_lefttop_absolute.x + position.x, - parent_lefttop_absolute.y + position.y - ); - control->position_cache_.lefttop_position_absolute = lefttop; - for(auto c : control->GetInternalChildren()) - { - RefreshControlPositionCacheInternal(c, lefttop); - } - } - - bool Control::IsPointInside(const Point & point) - { - const auto border_geometry = geometry_info_.border_geometry; - if (border_geometry != nullptr) - { - if (IsBordered()) - { - BOOL contains; - border_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains); - if (!contains) - border_geometry->StrokeContainsPoint(Convert(point), GetBorderProperty().GetStrokeWidth(), nullptr, D2D1::Matrix3x2F::Identity(), &contains); - return contains != 0; - } - else - { - BOOL contains; - border_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains); - return contains != 0; - } - } - return false; - } - - Control* Control::HitTest(const Point& point) - { - const auto point_inside = IsPointInside(point); - - if (IsClipContent()) - { - if (!point_inside) - return nullptr; - if (geometry_info_.content_geometry != nullptr) - { - BOOL contains; - ThrowIfFailed(geometry_info_.content_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains)); - if (contains == 0) - return this; - } - } - - const auto& children = GetInternalChildren(); - - for (auto i = children.crbegin(); i != children.crend(); ++i) - { - const auto&& lefttop = (*i)->GetOffset(); - const auto&& coerced_point = Point(point.x - lefttop.x, point.y - lefttop.y); - const auto child_hit_test_result = (*i)->HitTest(coerced_point); - if (child_hit_test_result != nullptr) - return child_hit_test_result; - } - - return point_inside ? this : nullptr; - } - - void Control::SetClipContent(const bool clip) - { - if (clip_content_ == clip) - return; - - clip_content_ = clip; - InvalidateDraw(); - } - - void Control::Draw(ID2D1DeviceContext* device_context) - { - D2D1::Matrix3x2F old_transform; - device_context->GetTransform(&old_transform); - - const auto position = GetOffset(); - device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y)); - - OnDrawDecoration(device_context); - - const auto set_layer = geometry_info_.content_geometry != nullptr && IsClipContent(); - if (set_layer) - device_context->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), geometry_info_.content_geometry.Get()), nullptr); - - OnDrawCore(device_context); - - for (auto child : GetInternalChildren()) - child->Draw(device_context); - - if (set_layer) - device_context->PopLayer(); - - device_context->SetTransform(old_transform); - } - - void Control::InvalidateDraw() - { - if (window_ != nullptr) - window_->InvalidateDraw(); - } - - bool Control::RequestFocus() - { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->RequestFocusFor(this); - } - - bool Control::HasFocus() - { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->GetFocusControl() == this; - } - - void Control::InvalidateLayout() - { - if (const auto window = GetWindow()) - window->WindowInvalidateLayout(); - } - - void Control::Measure(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - SetDesiredSize(OnMeasureCore(available_size, additional_info)); - } - - void Control::Layout(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - auto my_additional_info = additional_info; - my_additional_info.total_offset.x += rect.left; - my_additional_info.total_offset.y += rect.top; - position_cache_.lefttop_position_absolute.x = my_additional_info.total_offset.x; - position_cache_.lefttop_position_absolute.y = my_additional_info.total_offset.y; - - SetRect(rect); - OnLayoutCore(Rect(Point::Zero(), rect.GetSize()), my_additional_info); - } - - Size Control::GetDesiredSize() const - { - return desired_size_; - } - - void Control::SetDesiredSize(const Size& desired_size) - { - desired_size_ = desired_size; - } - - inline void Shrink(Rect& rect, const Thickness& thickness) - { - rect.left += thickness.left; - rect.top += thickness.top; - rect.width -= thickness.GetHorizontalTotal(); - rect.height -= thickness.GetVerticalTotal(); - } - - Rect Control::GetRect(const RectRange range) - { - if (GetSize() == Size::Zero()) - return Rect(); - - const auto layout_params = GetLayoutParams(); - - auto result = Rect(Point::Zero(), GetSize()); - - if (range == RectRange::Margin) - return result; - - Shrink(result, layout_params->margin); - - if (range == RectRange::FullBorder) - return result; - - if (is_bordered_) - Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); - - if (range == RectRange::HalfBorder) - return result; - - if (is_bordered_) - Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); - - if (range == RectRange::Padding) - return result; - - Shrink(result, layout_params->padding); - - return result; - } - - Point Control::TransformPoint(const Point& point, const RectRange from, const RectRange to) - { - const auto rect_from = GetRect(from); - const auto rect_to = GetRect(to); - auto p = point; - p.x += rect_from.left; - p.y += rect_from.top; - p.x -= rect_to.left; - p.y -= rect_to.top; - return p; - } - - void Control::UpdateBorder() - { - RegenerateGeometryInfo(); - InvalidateLayout(); - InvalidateDraw(); - } - - void Control::SetBordered(const bool bordered) - { - if (bordered != is_bordered_) - { - is_bordered_ = bordered; - UpdateBorder(); - } - } - - void Control::SetCursor(const Cursor::Ptr& cursor) - { - if (cursor != cursor_) - { - cursor_ = cursor; - const auto window = GetWindow(); - if (window && window->GetMouseHoverControl() == this) - window->UpdateCursor(); - } - } - - void Control::OnParentChanged(Control* old_parent, Control* new_parent) - { - - } - - void Control::OnInternalParentChanged(Control* old_internal_parent, Control* new_internal_parent) - { - - } - - void Control::OnAttachToWindow(Window* window) - { - window_ = window; - } - - void Control::OnDetachToWindow(Window * window) - { - window_ = nullptr; - } - - void Control::OnDrawDecoration(ID2D1DeviceContext* device_context) - { -#ifdef CRU_DEBUG_LAYOUT - if (GetWindow()->IsDebugLayout()) - { - if (padding_geometry_ != nullptr) - device_context->FillGeometry(padding_geometry_.Get(), UiManager::GetInstance()->GetPredefineResources()->debug_layout_padding_brush.Get()); - if (margin_geometry_ != nullptr) - device_context->FillGeometry(margin_geometry_.Get(), UiManager::GetInstance()->GetPredefineResources()->debug_layout_margin_brush.Get()); - device_context->DrawRectangle(Convert(GetRect(RectRange::Margin)), UiManager::GetInstance()->GetPredefineResources()->debug_layout_out_border_brush.Get()); - } -#endif - - if (is_bordered_ && geometry_info_.border_geometry != nullptr) - device_context->DrawGeometry( - geometry_info_.border_geometry.Get(), - GetBorderProperty().GetBrush().Get(), - GetBorderProperty().GetStrokeWidth(), - GetBorderProperty().GetStrokeStyle().Get() - ); - } - - void Control::OnDrawCore(ID2D1DeviceContext* device_context) - { - const auto ground_geometry = geometry_info_.padding_content_geometry; - //draw background. - if (ground_geometry != nullptr && background_brush_ != nullptr) - device_context->FillGeometry(ground_geometry.Get(), background_brush_.Get()); - const auto padding_rect = GetRect(RectRange::Padding); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(padding_rect.left, padding_rect.top), - [this](ID2D1DeviceContext* device_context) - { - events::DrawEventArgs args(this, this, device_context); - draw_background_event.Raise(args); - }); - - - const auto rect = GetRect(RectRange::Content); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top), - [this](ID2D1DeviceContext* device_context) - { - events::DrawEventArgs args(this, this, device_context); - draw_content_event.Raise(args); - }); - - - //draw foreground. - if (ground_geometry != nullptr && foreground_brush_ != nullptr) - device_context->FillGeometry(ground_geometry.Get(), foreground_brush_.Get()); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(padding_rect.left, padding_rect.top), - [this](ID2D1DeviceContext* device_context) - { - events::DrawEventArgs args(this, this, device_context); - draw_foreground_event.Raise(args); - }); - } - - void Control::OnRectChange(const Rect& old_rect, const Rect& new_rect) - { - - } - - void Control::RegenerateGeometryInfo() - { - if (IsBordered()) - { - const auto bound_rect = GetRect(RectRange::HalfBorder); - const auto bound_rounded_rect = D2D1::RoundedRect(Convert(bound_rect), - GetBorderProperty().GetRadiusX(), - GetBorderProperty().GetRadiusY()); - - Microsoft::WRL::ComPtr<ID2D1RoundedRectangleGeometry> geometry; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(bound_rounded_rect, &geometry) - ); - geometry_info_.border_geometry = std::move(geometry); - - const auto padding_rect = GetRect(RectRange::Padding); - const auto in_border_rounded_rect = D2D1::RoundedRect(Convert(padding_rect), - GetBorderProperty().GetRadiusX() - GetBorderProperty().GetStrokeWidth() / 2.0f, - GetBorderProperty().GetRadiusY() - GetBorderProperty().GetStrokeWidth() / 2.0f); - - Microsoft::WRL::ComPtr<ID2D1RoundedRectangleGeometry> geometry2; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(in_border_rounded_rect, &geometry2) - ); - geometry_info_.padding_content_geometry = geometry2; - - - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> geometry3; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(GetRect(RectRange::Content)), &geometry3) - ); - Microsoft::WRL::ComPtr<ID2D1PathGeometry> geometry4; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreatePathGeometry(&geometry4) - ); - Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink; - geometry4->Open(&sink); - ThrowIfFailed( - geometry3->CombineWithGeometry(geometry2.Get(), D2D1_COMBINE_MODE_INTERSECT, D2D1::Matrix3x2F::Identity(), sink.Get()) - ); - sink->Close(); - geometry_info_.content_geometry = std::move(geometry4); - } - else - { - const auto bound_rect = GetRect(RectRange::Padding); - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> geometry; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(bound_rect), &geometry) - ); - geometry_info_.border_geometry = geometry; - geometry_info_.padding_content_geometry = std::move(geometry); - - Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> geometry2; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(GetRect(RectRange::Content)), &geometry2) - ); - geometry_info_.content_geometry = std::move(geometry2); - } - - //TODO: generate debug geometry -#ifdef CRU_DEBUG_LAYOUT - margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder)); - padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content)); -#endif - } - - void Control::OnMouseClickBegin(MouseButton button) - { - } - - void Control::OnMouseClickEnd(MouseButton button) - { - } - - inline Size ThicknessToSize(const Thickness& thickness) - { - return Size(thickness.left + thickness.right, thickness.top + thickness.bottom); - } - - Size Control::OnMeasureCore(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - const auto layout_params = GetLayoutParams(); - - if (!layout_params->Validate()) - throw std::runtime_error("LayoutParams is not valid. Please check it."); - - auto my_additional_info = additional_info; - - if (layout_params->width.mode == MeasureMode::Content) - my_additional_info.horizontal_stretchable = false; - else if (layout_params->width.mode == MeasureMode::Exactly) - my_additional_info.horizontal_stretchable = true; - // if stretch, then inherent parent's value - - if (layout_params->height.mode == MeasureMode::Content) - my_additional_info.vertical_stretchable = false; - else if (layout_params->height.mode == MeasureMode::Exactly) - my_additional_info.vertical_stretchable = true; - // if stretch, then inherent parent's value - - - auto border_size = Size::Zero(); - if (is_bordered_) - { - const auto border_width = GetBorderProperty().GetStrokeWidth(); - border_size = Size(border_width * 2.0f, border_width * 2.0f); - } - - // the total size of padding, border and margin - const auto outer_size = ThicknessToSize(layout_params->padding) + - ThicknessToSize(layout_params->margin) + border_size; - - - auto&& get_content_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float outer_length) -> float - { - float length; - if (layout_length.mode == MeasureMode::Exactly) - length = layout_length.length; - else if (available_length > outer_length) - length = available_length - outer_length; - else - length = 0; - return Coerce(length, layout_length.min, layout_length.max); - }; - - // if padding, margin and border exceeded, then content size is 0. - const auto content_measure_size = Size( - get_content_measure_length(layout_params->width, available_size.width, outer_size.width), - get_content_measure_length(layout_params->height, available_size.height, outer_size.height) - ); - - const auto content_actual_size = OnMeasureContent(content_measure_size, my_additional_info); - - - - auto&& calculate_final_length = [](const bool stretch, const std::optional<float> min_length, const float measure_length, const float actual_length) -> float - { - // only use measure length when stretch and actual length is smaller than measure length, that is "stretch" - if (stretch && actual_length < measure_length) - return measure_length; - return Coerce(actual_length, min_length, std::nullopt); - }; - - const auto final_size = Size( - calculate_final_length(my_additional_info.horizontal_stretchable, layout_params->width.min, content_measure_size.width, content_actual_size.width), - calculate_final_length(my_additional_info.vertical_stretchable, layout_params->height.min, content_measure_size.height, content_actual_size.height) - ) + outer_size; - - return final_size; - } - - void Control::OnLayoutCore(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - const auto layout_params = GetLayoutParams(); - - auto border_width = 0.0f; - if (is_bordered_) - { - border_width = GetBorderProperty().GetStrokeWidth(); - } - - const Rect content_rect( - rect.left + layout_params->padding.left + layout_params->margin.right + border_width, - rect.top + layout_params->padding.top + layout_params->margin.top + border_width, - rect.width - layout_params->padding.GetHorizontalTotal() - layout_params->margin.GetHorizontalTotal() - border_width * 2.0f, - rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() - border_width * 2.0f - ); - - if (content_rect.width < 0.0) - throw std::runtime_error(Format("Width to layout must sufficient. But in {}, width for content is {}.", ToUtf8String(GetControlType()), content_rect.width)); - if (content_rect.height < 0.0) - throw std::runtime_error(Format("Height to layout must sufficient. But in {}, height for content is {}.", ToUtf8String(GetControlType()), content_rect.height)); - - OnLayoutContent(content_rect, additional_info); - } - - const std::vector<Control*> NoChildControl::empty_control_vector{}; - - std::list<Control*> GetAncestorList(Control* control) - { - std::list<Control*> l; - while (control != nullptr) - { - l.push_front(control); - control = control->GetInternalParent(); - } - return l; - } - - void NoChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - - } - - SingleChildControl::SingleChildControl() : child_vector_{nullptr}, child_(child_vector_[0]) - { - - } - - SingleChildControl::~SingleChildControl() - { - delete child_; - } - - void SingleChildControl::SetChild(Control* child) - { - if (child == child_) - return; - - const auto window = GetWindow(); - const auto old_child = child_; - child_ = child; - if (old_child) - { - old_child->SetInternalParent(nullptr); - old_child->SetDescendantWindow(nullptr); - } - if (child) - { - child->SetInternalParent(this); - child->SetDescendantWindow(window); - } - OnChildChanged(old_child, child); - } - - void SingleChildControl::OnChildChanged(Control* old_child, Control* new_child) - { - - } - - Size SingleChildControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - auto child_size = Size::Zero(); - if (child_) - { - child_->Measure(available_size, additional_info); - child_size = child_->GetDesiredSize(); - } - - return child_size; - } - - void SingleChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - if (child_) - { - const auto layout_params = child_->GetLayoutParams(); - const auto size = child_->GetDesiredSize(); - - auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float - { - switch (alignment) - { - case Alignment::Center: - return anchor + (layout_length - control_length) / 2; - case Alignment::Start: - return anchor; - case Alignment::End: - return anchor + layout_length - control_length; - default: - UnreachableCode(); - } - }; - - child_->Layout(Rect(Point( - calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width), - calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height) - ), size), additional_info); - } - } - - void AddChildCheck(Control* control) - { - if (control->GetInternalParent() != nullptr) - throw std::invalid_argument("The control already has a parent."); - - if (dynamic_cast<Window*>(control)) - throw std::invalid_argument("Can't add a window as child."); - } - - MultiChildControl::~MultiChildControl() - { - for (const auto child : children_) - delete child; - } - - void MultiChildControl::AddChild(Control* control) - { - AddChildCheck(control); - - children_.push_back(control); - - control->SetInternalParent(this); - control->SetDescendantWindow(GetWindow()); - - OnAddChild(control); - } - - void MultiChildControl::AddChild(Control* control, const int position) - { - AddChildCheck(control); - - if (position < 0 || static_cast<decltype(children_.size())>(position) > this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - children_.insert(this->children_.cbegin() + position, control); - - control->SetInternalParent(this); - control->SetDescendantWindow(GetWindow()); - - OnAddChild(control); - } - - void MultiChildControl::RemoveChild(Control* child) - { - const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child); - if (i == this->children_.cend()) - throw std::invalid_argument("The argument child is not a child of this control."); - - children_.erase(i); - - child->SetInternalParent(nullptr); - child->SetDescendantWindow(nullptr); - - OnRemoveChild(child); - } - - void MultiChildControl::RemoveChild(const int position) - { - if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - const auto i = children_.cbegin() + position; - const auto child = *i; - - children_.erase(i); - - child->SetInternalParent(nullptr); - child->SetDescendantWindow(nullptr); - - OnRemoveChild(child); - } - - void MultiChildControl::OnAddChild(Control* child) - { - - } - - void MultiChildControl::OnRemoveChild(Control* child) - { - - } - - Control* FindLowestCommonAncestor(Control * left, Control * right) - { - if (left == nullptr || right == nullptr) - return nullptr; - - auto&& left_list = GetAncestorList(left); - auto&& right_list = GetAncestorList(right); - - // the root is different - if (left_list.front() != right_list.front()) - return nullptr; - - // find the last same control or the last control (one is ancestor of the other) - auto left_i = left_list.cbegin(); - auto right_i = right_list.cbegin(); - while (true) - { - if (left_i == left_list.cend()) - return *(--left_i); - if (right_i == right_list.cend()) - return *(--right_i); - if (*left_i != *right_i) - return *(--left_i); - ++left_i; - ++right_i; - } - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\control.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\cursor.cpp -//-------------------------------------------------------- - - -namespace cru::ui -{ - Cursor::Cursor(HCURSOR handle, const bool auto_release) - : handle_(handle), auto_release_(auto_release) - { - - } - - Cursor::~Cursor() - { - if (auto_release_) - ::DestroyCursor(handle_); - } - - namespace cursors - { - Cursor::Ptr arrow{}; - Cursor::Ptr hand{}; - Cursor::Ptr i_beam{}; - - void LoadSystemCursors() - { - arrow = std::make_shared<Cursor>(::LoadCursorW(nullptr, IDC_ARROW), false); - hand = std::make_shared<Cursor>(::LoadCursorW(nullptr, IDC_HAND), false); - i_beam = std::make_shared<Cursor>(::LoadCursorW(nullptr, IDC_IBEAM), false); - } - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\cursor.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\layout_base.cpp -//-------------------------------------------------------- - -namespace cru::ui -{ - -} -//-------------------------------------------------------- -//-------end of file: src\ui\layout_base.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\ui_base.cpp -//-------------------------------------------------------- - - -namespace cru::ui -{ - bool IsKeyDown(const int virtual_code) - { - const auto result = ::GetKeyState(virtual_code); - return (static_cast<unsigned short>(result) & 0x8000) != 0; - } - - bool IsKeyToggled(const int virtual_code) - { - const auto result = ::GetKeyState(virtual_code); - return (static_cast<unsigned short>(result) & 1) != 0; - } - - bool IsAnyMouseButtonDown() - { - return IsKeyDown(VK_LBUTTON) || IsKeyDown(VK_RBUTTON) || IsKeyDown(VK_MBUTTON); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\ui_base.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\ui_manager.cpp -//-------------------------------------------------------- - - - -namespace cru::ui -{ - namespace - { - void GetSystemCaretInfo(CaretInfo* caret_info) - { - caret_info->caret_blink_duration = std::chrono::milliseconds(::GetCaretBlinkTime()); - DWORD caret_width; - if (!::SystemParametersInfoW(SPI_GETCARETWIDTH, 0 , &caret_width, 0)) - throw Win32Error(::GetLastError(), "Failed to get system caret width."); - caret_info->half_caret_width = caret_width / 2.0f; - } - - Microsoft::WRL::ComPtr<ID2D1Brush> CreateSolidBrush(graph::GraphManager* graph_manager, const D2D1_COLOR_F& color) - { - const auto device_context = graph_manager->GetD2D1DeviceContext(); - Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> solid_color_brush; - device_context->CreateSolidColorBrush(color, &solid_color_brush); - return solid_color_brush; - } - - Microsoft::WRL::ComPtr<IDWriteTextFormat> CreateDefaultTextFormat(graph::GraphManager* graph_manager) - { - const auto dwrite_factory = graph_manager->GetDWriteFactory(); - - Microsoft::WRL::ComPtr<IDWriteTextFormat> text_format; - - ThrowIfFailed(dwrite_factory->CreateTextFormat( - L"等线", nullptr, - DWRITE_FONT_WEIGHT_NORMAL, - DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, - 24.0, L"zh-cn", - &text_format - )); - - ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); - ThrowIfFailed(text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); - - return text_format; - } - } - - - //!!! never use default constructor of border at here, because it will recursively call this method! - PredefineResources::PredefineResources(graph::GraphManager* graph_manager) : - border_property_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - - button_normal_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::RoyalBlue)), 2, 6, 6}, - button_press_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Blue)), 2, 6, 6}, - - text_control_selection_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightSkyBlue))}, - - text_box_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_box_text_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_box_text_format {CreateDefaultTextFormat(graph_manager)}, - text_box_caret_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - - text_block_text_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_block_text_format {CreateDefaultTextFormat(graph_manager)}, - - toggle_button_on_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DeepSkyBlue))}, - toggle_button_off_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightGray))}, - - list_item_normal_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::White, 0))}, - list_item_normal_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::White, 0))}, - list_item_hover_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue))}, - list_item_hover_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))}, - list_item_select_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::MediumBlue))}, - list_item_select_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))}, - - scroll_bar_background_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Gainsboro, 0.3f))}, - scroll_bar_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DimGray))}, - scroll_bar_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DimGray))} - -#ifdef CRU_DEBUG_LAYOUT - , - debug_layout_out_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Crimson))}, - debug_layout_margin_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f))}, - debug_layout_padding_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f))} -#endif - { - - } - - UiManager* UiManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton<UiManager>([](auto) - { - return new UiManager{}; - }); - } - - UiManager::UiManager() - : predefine_resources_(graph::GraphManager::GetInstance()) - { - GetSystemCaretInfo(&caret_info_); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\ui_manager.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\window.cpp -//-------------------------------------------------------- - - -namespace cru::ui -{ - WindowClass::WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance) - : name_(name) - { - WNDCLASSEX window_class; - window_class.cbSize = sizeof(WNDCLASSEX); - - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.lpfnWndProc = window_proc; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = h_instance; - window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); - window_class.hCursor = LoadCursor(NULL, IDC_ARROW); - window_class.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); - window_class.lpszMenuName = NULL; - window_class.lpszClassName = name.c_str(); - window_class.hIconSm = NULL; - - atom_ = RegisterClassEx(&window_class); - if (atom_ == 0) - throw std::runtime_error("Failed to create window class."); - } - - LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { - auto window = WindowManager::GetInstance()->FromHandle(hWnd); - - LRESULT result; - if (window != nullptr && window->HandleWindowMessage(hWnd, Msg, wParam, lParam, result)) - return result; - - return DefWindowProc(hWnd, Msg, wParam, lParam); - } - - WindowManager* WindowManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton<WindowManager>([](auto) - { - return new WindowManager{}; - }); - } - - WindowManager::WindowManager() { - general_window_class_ = std::make_unique<WindowClass>( - L"CruUIWindowClass", - GeneralWndProc, - Application::GetInstance()->GetInstanceHandle() - ); - } - - void WindowManager::RegisterWindow(HWND hwnd, Window * window) { - const auto find_result = window_map_.find(hwnd); - if (find_result != window_map_.end()) - throw std::runtime_error("The hwnd is already in the map."); - - window_map_.emplace(hwnd, window); - } - - void WindowManager::UnregisterWindow(HWND hwnd) { - const auto find_result = window_map_.find(hwnd); - if (find_result == window_map_.end()) - throw std::runtime_error("The hwnd is not in the map."); - window_map_.erase(find_result); - - if (window_map_.empty()) - Application::GetInstance()->Quit(0); - } - - Window* WindowManager::FromHandle(HWND hwnd) { - const auto find_result = window_map_.find(hwnd); - if (find_result == window_map_.end()) - return nullptr; - else - return find_result->second; - } - - std::vector<Window*> WindowManager::GetAllWindows() const - { - std::vector<Window*> windows; - for (auto [key, value] : window_map_) - windows.push_back(value); - return windows; - } - - inline Point PiToDip(const POINT& pi_point) - { - return Point( - graph::PixelToDipX(pi_point.x), - graph::PixelToDipY(pi_point.y) - ); - } - - inline POINT DipToPi(const Point& dip_point) - { - POINT result; - result.x = graph::DipToPixelX(dip_point.x); - result.y = graph::DipToPixelY(dip_point.y); - return result; - } - - - namespace - { - Cursor::Ptr GetCursorInherit(Control* control) - { - while (control != nullptr) - { - const auto cursor = control->GetCursor(); - if (cursor != nullptr) - return cursor; - control = control->GetInternalParent(); - } - return cursors::arrow; - } - } - - Window* Window::CreateOverlapped() - { - return new Window(tag_overlapped_constructor{}); - } - - Window* Window::CreatePopup(Window* parent, const bool caption) - { - return new Window(tag_popup_constructor{}, parent, caption); - } - - - Window::Window(tag_overlapped_constructor) - { - BeforeCreateHwnd(); - - const auto window_manager = WindowManager::GetInstance(); - - hwnd_ = CreateWindowEx(0, - window_manager->GetGeneralWindowClass()->GetName(), - L"", WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - nullptr, nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr - ); - - if (hwnd_ == nullptr) - throw std::runtime_error("Failed to create window."); - - AfterCreateHwnd(window_manager); - } - - Window::Window(tag_popup_constructor, Window* parent, const bool caption) - { - if (parent != nullptr && !parent->IsWindowValid()) - throw std::runtime_error("Parent window is not valid."); - - BeforeCreateHwnd(); - - parent_window_ = parent; - - const auto window_manager = WindowManager::GetInstance(); - - hwnd_ = CreateWindowEx(0, - window_manager->GetGeneralWindowClass()->GetName(), - L"", caption ? (WS_POPUPWINDOW | WS_CAPTION) : WS_POPUP, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - parent == nullptr ? nullptr : parent->GetWindowHandle(), - nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr - ); - - if (hwnd_ == nullptr) - throw std::runtime_error("Failed to create window."); - - AfterCreateHwnd(window_manager); - } - - void Window::BeforeCreateHwnd() - { - window_ = this; - } - - void Window::AfterCreateHwnd(WindowManager* window_manager) - { - window_manager->RegisterWindow(hwnd_, this); - - render_target_ = graph::GraphManager::GetInstance()->CreateWindowRenderTarget(hwnd_); - - SetCursor(cursors::arrow); - } - - Window::~Window() { - if (IsWindowValid()) - { - SetDeleteThisOnDestroy(false); // avoid double delete. - Close(); - } - TraverseDescendants([this](Control* control) { - control->OnDetachToWindow(this); - }); - } - - StringView Window::GetControlType() const - { - return control_type; - } - - void Window::SetDeleteThisOnDestroy(bool value) - { - delete_this_on_destroy_ = value; - } - - void Window::Close() { - if (IsWindowValid()) - DestroyWindow(hwnd_); - } - - void Window::InvalidateDraw() { - if (IsWindowValid()) { - InvalidateRect(hwnd_, nullptr, false); - } - } - - void Window::Show() { - if (IsWindowValid()) { - ShowWindow(hwnd_, SW_SHOWNORMAL); - } - } - - void Window::Hide() { - if (IsWindowValid()) { - ShowWindow(hwnd_, SW_HIDE); - } - } - - Size Window::GetClientSize() { - if (!IsWindowValid()) - return Size(); - - const auto pixel_rect = GetClientRectPixel(); - return Size( - graph::PixelToDipX(pixel_rect.right), - graph::PixelToDipY(pixel_rect.bottom) - ); - } - - void Window::SetClientSize(const Size & size) { - if (IsWindowValid()) { - const auto window_style = static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_STYLE)); - const auto window_ex_style = static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_EXSTYLE)); - - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = graph::DipToPixelX(size.width); - rect.bottom = graph::DipToPixelY(size.height); - AdjustWindowRectEx(&rect, window_style, FALSE, window_ex_style); - - SetWindowPos( - hwnd_, nullptr, 0, 0, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOZORDER | SWP_NOMOVE - ); - } - } - - Rect Window::GetWindowRect() { - if (!IsWindowValid()) - return Rect(); - - RECT rect; - ::GetWindowRect(hwnd_, &rect); - - return Rect::FromVertices( - graph::PixelToDipX(rect.left), - graph::PixelToDipY(rect.top), - graph::PixelToDipX(rect.right), - graph::PixelToDipY(rect.bottom) - ); - } - - void Window::SetWindowRect(const Rect & rect) { - if (IsWindowValid()) { - SetWindowPos( - hwnd_, nullptr, - graph::DipToPixelX(rect.left), - graph::DipToPixelY(rect.top), - graph::DipToPixelX(rect.GetRight()), - graph::DipToPixelY(rect.GetBottom()), - SWP_NOZORDER - ); - } - } - - void Window::SetWindowPosition(const Point& position) - { - if (IsWindowValid()) { - SetWindowPos( - hwnd_, nullptr, - graph::DipToPixelX(position.x), - graph::DipToPixelY(position.y), - 0, 0, - SWP_NOZORDER | SWP_NOSIZE - ); - } - } - - Point Window::PointToScreen(const Point& point) - { - if (!IsWindowValid()) - return Point::Zero(); - - auto p = DipToPi(point); - if (::ClientToScreen(GetWindowHandle(), &p) == 0) - throw Win32Error(::GetLastError(), "Failed transform point from window to screen."); - return PiToDip(p); - } - - Point Window::PointFromScreen(const Point& point) - { - if (!IsWindowValid()) - return Point::Zero(); - - auto p = DipToPi(point); - if (::ScreenToClient(GetWindowHandle(), &p) == 0) - throw Win32Error(::GetLastError(), "Failed transform point from screen to window."); - return PiToDip(p); - } - - bool Window::HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT & result) { - - events::WindowNativeMessageEventArgs args(this, this, {hwnd, msg, w_param, l_param}); - native_message_event.Raise(args); - if (args.GetResult().has_value()) - { - result = args.GetResult().value(); - return true; - } - - switch (msg) { - case WM_PAINT: - OnPaintInternal(); - result = 0; - return true; - case WM_ERASEBKGND: - result = 1; - return true; - case WM_SETFOCUS: - OnSetFocusInternal(); - result = 0; - return true; - case WM_KILLFOCUS: - OnKillFocusInternal(); - result = 0; - return true; - case WM_MOUSEMOVE: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseMoveInternal(point); - result = 0; - return true; - } - case WM_LBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Left, point); - result = 0; - return true; - } - case WM_LBUTTONUP: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Left, point); - result = 0; - return true; - } - case WM_RBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Right, point); - result = 0; - return true; - } - case WM_RBUTTONUP: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Right, point); - result = 0; - return true; - } - case WM_MBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Middle, point); - result = 0; - return true; - } - case WM_MBUTTONUP: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Middle, point); - result = 0; - return true; - } - case WM_MOUSEWHEEL: - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - ScreenToClient(hwnd, &point); - OnMouseWheelInternal(GET_WHEEL_DELTA_WPARAM(w_param), point); - result = 0; - return true; - case WM_KEYDOWN: - OnKeyDownInternal(static_cast<int>(w_param)); - result = 0; - return true; - case WM_KEYUP: - OnKeyUpInternal(static_cast<int>(w_param)); - result = 0; - return true; - case WM_CHAR: - OnCharInternal(static_cast<wchar_t>(w_param)); - result = 0; - return true; - case WM_SIZE: - OnResizeInternal(LOWORD(l_param), HIWORD(l_param)); - result = 0; - return true; - case WM_ACTIVATE: - if (w_param == WA_ACTIVE || w_param == WA_CLICKACTIVE) - OnActivatedInternal(); - else if (w_param == WA_INACTIVE) - OnDeactivatedInternal(); - result = 0; - return true; - case WM_DESTROY: - OnDestroyInternal(); - result = 0; - return true; - default: - return false; - } - } - - Point Window::GetMousePosition() - { - if (!IsWindowValid()) - return Point::Zero(); - POINT point; - ::GetCursorPos(&point); - ::ScreenToClient(hwnd_, &point); - return PiToDip(point); - } - - Point Window::GetOffset() - { - return Point(); - } - - Size Window::GetSize() - { - return GetClientSize(); - } - - void Window::SetRect(const Rect& size) - { - - } - - bool Window::IsPointInside(const Point& point) - { - return Rect(Point::Zero(), GetClientSize()).IsPointInside(point); - } - - void Window::WindowInvalidateLayout() - { - if (is_layout_invalid_) - return; - - is_layout_invalid_ = true; - InvokeLater([this] - { - if (is_layout_invalid_) - Relayout(); - }); - } - - void Window::Relayout() - { - Measure(GetSize(), AdditionalMeasureInfo{}); - OnLayoutCore(Rect(Point::Zero(), GetSize()), AdditionalLayoutInfo{}); - is_layout_invalid_ = false; - } - - void Window::SetSizeFitContent(const Size& max_size) - { - Measure(max_size, AdditionalMeasureInfo{}); - SetClientSize(GetDesiredSize()); - OnLayoutCore(Rect(Point::Zero(), GetSize()), AdditionalLayoutInfo{}); - is_layout_invalid_ = false; - } - - bool Window::RequestFocusFor(Control * control) - { - if (control == nullptr) - throw std::invalid_argument("The control to request focus can't be null. You can set it as the window."); - - if (!IsWindowValid()) - return false; - - if (!window_focus_) - { - focus_control_ = control; - ::SetFocus(hwnd_); - return true; // event dispatch will be done in window message handling function "OnSetFocusInternal". - } - - if (focus_control_ == control) - return true; - - DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, false); - - focus_control_ = control; - - DispatchEvent(control, &Control::get_focus_event, nullptr, false); - - return true; - } - - Control* Window::GetFocusControl() - { - return focus_control_; - } - - Control* Window::CaptureMouseFor(Control* control) - { - if (control != nullptr) - { - ::SetCapture(hwnd_); - std::swap(mouse_capture_control_, control); - DispatchMouseHoverControlChangeEvent(control ? control : mouse_hover_control_, mouse_capture_control_, GetMousePosition()); - return control; - } - else - { - return ReleaseCurrentMouseCapture(); - } - } - - Control* Window::ReleaseCurrentMouseCapture() - { - if (mouse_capture_control_) - { - const auto previous = mouse_capture_control_; - mouse_capture_control_ = nullptr; - ::ReleaseCapture(); - DispatchMouseHoverControlChangeEvent(previous, mouse_hover_control_, GetMousePosition()); - return previous; - } - else - { - return nullptr; - } - } - - void Window::UpdateCursor() - { - if (IsWindowValid() && mouse_hover_control_ != nullptr) - { - SetCursorInternal(GetCursorInherit(mouse_hover_control_)->GetHandle()); - } - } - -#ifdef CRU_DEBUG_LAYOUT - void Window::SetDebugLayout(const bool value) - { - if (debug_layout_ != value) - { - debug_layout_ = value; - InvalidateDraw(); - } - } -#endif - - RECT Window::GetClientRectPixel() { - RECT rect{ }; - GetClientRect(hwnd_, &rect); - return rect; - } - - bool Window::IsMessageInQueue(UINT message) - { - MSG msg; - return ::PeekMessageW(&msg, hwnd_, message, message, PM_NOREMOVE) != 0; - } - - void Window::SetCursorInternal(HCURSOR cursor) - { - if (IsWindowValid()) - { - ::SetClassLongPtrW(GetWindowHandle(), GCLP_HCURSOR, reinterpret_cast<LONG_PTR>(cursor)); - if (mouse_hover_control_ != nullptr) - ::SetCursor(cursor); - } - } - - void Window::OnDestroyInternal() { - WindowManager::GetInstance()->UnregisterWindow(hwnd_); - hwnd_ = nullptr; - if (delete_this_on_destroy_) - InvokeLater([this]{ delete this; }); - } - - void Window::OnPaintInternal() { - render_target_->SetAsTarget(); - - auto device_context = render_target_->GetD2DDeviceContext(); - - device_context->BeginDraw(); - - //Clear the background. - device_context->Clear(D2D1::ColorF(D2D1::ColorF::White)); - - Draw(device_context.Get()); - - ThrowIfFailed( - device_context->EndDraw(), "Failed to draw window." - ); - - render_target_->Present(); - - ValidateRect(hwnd_, nullptr); - } - - void Window::OnResizeInternal(const int new_width, const int new_height) { - render_target_->ResizeBuffer(new_width, new_height); - if (!(new_width == 0 && new_height == 0)) - WindowInvalidateLayout(); - } - - void Window::OnSetFocusInternal() - { - window_focus_ = true; - DispatchEvent(focus_control_, &Control::get_focus_event, nullptr, true); - } - - void Window::OnKillFocusInternal() - { - window_focus_ = false; - DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, true); - } - - void Window::OnMouseMoveInternal(const POINT point) - { - const auto dip_point = PiToDip(point); - - //when mouse was previous outside the window - if (mouse_hover_control_ == nullptr) { - //invoke TrackMouseEvent to have WM_MOUSELEAVE sent. - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof tme; - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hwnd_; - - TrackMouseEvent(&tme); - } - - //Find the first control that hit test succeed. - const auto new_control_mouse_hover = HitTest(dip_point); - const auto old_control_mouse_hover = mouse_hover_control_; - mouse_hover_control_ = new_control_mouse_hover; - - if (mouse_capture_control_) // if mouse is captured - { - DispatchEvent(mouse_capture_control_, &Control::mouse_move_event, nullptr, dip_point); - } - else - { - DispatchMouseHoverControlChangeEvent(old_control_mouse_hover, new_control_mouse_hover, dip_point); - DispatchEvent(new_control_mouse_hover, &Control::mouse_move_event, nullptr, dip_point); - } - } - - void Window::OnMouseLeaveInternal() - { - DispatchEvent(mouse_hover_control_, &Control::mouse_leave_event, nullptr); - mouse_hover_control_ = nullptr; - } - - void Window::OnMouseDownInternal(MouseButton button, POINT point) - { - const auto dip_point = PiToDip(point); - - Control* control; - - if (mouse_capture_control_) - control = mouse_capture_control_; - else - control = HitTest(dip_point); - - DispatchEvent(control, &Control::mouse_down_event, nullptr, dip_point, button); - } - - void Window::OnMouseUpInternal(MouseButton button, POINT point) - { - const auto dip_point = PiToDip(point); - - Control* control; - - if (mouse_capture_control_) - control = mouse_capture_control_; - else - control = HitTest(dip_point); - - DispatchEvent(control, &Control::mouse_up_event, nullptr, dip_point, button); - } - - void Window::OnMouseWheelInternal(short delta, POINT point) - { - const auto dip_point = PiToDip(point); - - Control* control; - - if (mouse_capture_control_) - control = mouse_capture_control_; - else - control = HitTest(dip_point); - - DispatchEvent(control, &Control::mouse_wheel_event, nullptr, dip_point, static_cast<float>(delta)); - } - - void Window::OnKeyDownInternal(int virtual_code) - { - DispatchEvent(focus_control_, &Control::key_down_event, nullptr, virtual_code); - } - - void Window::OnKeyUpInternal(int virtual_code) - { - DispatchEvent(focus_control_, &Control::key_up_event, nullptr, virtual_code); - } - - void Window::OnCharInternal(wchar_t c) - { - DispatchEvent(focus_control_, &Control::char_event, nullptr, c); - } - - void Window::OnActivatedInternal() - { - events::UiEventArgs args(this, this); - activated_event.Raise(args); - } - - void Window::OnDeactivatedInternal() - { - events::UiEventArgs args(this, this); - deactivated_event.Raise(args); - } - - void Window::DispatchMouseHoverControlChangeEvent(Control* old_control, Control* new_control, const Point& point) - { - if (new_control != old_control) //if the mouse-hover-on control changed - { - const auto lowest_common_ancestor = FindLowestCommonAncestor(old_control, new_control); - if (old_control != nullptr) // if last mouse-hover-on control exists - DispatchEvent(old_control, &Control::mouse_leave_event, lowest_common_ancestor); // dispatch mouse leave event. - if (new_control != nullptr) - { - DispatchEvent(new_control, &Control::mouse_enter_event, lowest_common_ancestor, point); // dispatch mouse enter event. - UpdateCursor(); - } - } - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\window.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\animations\animation.cpp -//-------------------------------------------------------- - -#include <utility> - - -namespace cru::ui::animations -{ - namespace details - { - class AnimationDelegateImpl; - constexpr double frame_rate = 60; - constexpr AnimationTimeUnit frame_step_time = AnimationTimeUnit(1) / frame_rate; - - - class AnimationDelegateImpl : public virtual IAnimationDelegate - { - public: - explicit AnimationDelegateImpl(String tag) - : tag_(std::move(tag)) - { - - } - AnimationDelegateImpl(const AnimationDelegateImpl& other) = delete; - AnimationDelegateImpl(AnimationDelegateImpl&& other) = delete; - AnimationDelegateImpl& operator=(const AnimationDelegateImpl& other) = delete; - AnimationDelegateImpl& operator=(AnimationDelegateImpl&& other) = delete; - ~AnimationDelegateImpl() override = default; - - void Cancel() override - { - AnimationManager::GetInstance()->RemoveAnimation(tag_); - } - - private: - String tag_; - }; - - - class Animation : public Object - { - public: - Animation(AnimationInfo info, AnimationDelegatePtr delegate) - : info_(std::move(info)), delegate_(std::move(delegate)) - { - - } - - Animation(const Animation& other) = delete; - Animation(Animation&& other) = delete; - Animation& operator=(const Animation& other) = delete; - Animation& operator=(Animation&& other) = delete; - ~Animation() override; - - - // If finish or invalid, return false. - bool Step(AnimationTimeUnit time); - - String GetTag() const - { - return info_.tag; - } - - private: - const AnimationInfo info_; - const AnimationDelegatePtr delegate_; - - AnimationTimeUnit current_time_ = AnimationTimeUnit::zero(); - }; - - AnimationManager* AnimationManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton<AnimationManager>([](auto) - { - return new AnimationManager{}; - }); - } - - AnimationManager::AnimationManager() - { - - } - - AnimationManager::~AnimationManager() - { - KillTimer(); - } - - AnimationDelegatePtr AnimationManager::CreateAnimation(AnimationInfo info) - { - if (animations_.empty()) - SetTimer(); - - const auto tag = info.tag; - auto delegate = std::make_shared<AnimationDelegateImpl>(tag); - animations_[tag] = std::make_unique<Animation>(std::move(info), delegate); - - return delegate; - } - - void AnimationManager::RemoveAnimation(const String& tag) - { - const auto find_result = animations_.find(tag); - if (find_result != animations_.cend()) - animations_.erase(find_result); - - if (animations_.empty()) - KillTimer(); - } - - void AnimationManager::SetTimer() - { - if (!timer_.has_value()) - timer_ = SetInterval(std::chrono::duration_cast<std::chrono::milliseconds>(frame_step_time), [this]() - { - auto i = animations_.cbegin(); - while (i != animations_.cend()) - { - auto current_i = i++; - if (current_i->second->Step(frame_step_time)) - animations_.erase(current_i); - } - - if (animations_.empty()) - KillTimer(); - }); - } - - void AnimationManager::KillTimer() - { - if (timer_.has_value()) - { - timer_.value().Cancel(); - timer_ = std::nullopt; - } - } - - Animation::~Animation() - { - if (current_time_ < info_.duration) - for (const auto& handler : info_.cancel_handlers) - handler(); - } - - bool Animation::Step(const AnimationTimeUnit time) - { - current_time_ += time; - if (current_time_ > info_.duration) - { - for (const auto& handler : info_.step_handlers) - handler(delegate_, 1); - for (const auto& handler : info_.finish_handlers) - handler(); - return true; - } - else - { - for (const auto& handler : info_.step_handlers) - handler(delegate_, current_time_ / info_.duration); - return false; - } - } - - } - - AnimationDelegatePtr AnimationBuilder::Start() - { - CheckValid(); - valid_ = false; - return details::AnimationManager::GetInstance()->CreateAnimation(std::move(info_)); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\animations\animation.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\button.cpp -//-------------------------------------------------------- - - -namespace cru::ui::controls -{ - Button::Button() : - normal_border_{UiManager::GetInstance()->GetPredefineResources()->button_normal_border}, - pressed_border_{UiManager::GetInstance()->GetPredefineResources()->button_press_border} - { - SetBordered(true); - GetBorderProperty() = normal_border_; - - SetCursor(cursors::hand); - } - - StringView Button::GetControlType() const - { - return control_type; - } - - void Button::OnMouseClickBegin(MouseButton button) - { - GetBorderProperty() = pressed_border_; - UpdateBorder(); - } - - void Button::OnMouseClickEnd(MouseButton button) - { - GetBorderProperty() = normal_border_; - UpdateBorder(); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\button.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\frame_layout.cpp -//-------------------------------------------------------- - -namespace cru::ui::controls -{ - FrameLayout::FrameLayout() = default; - - FrameLayout::~FrameLayout() = default; - - StringView FrameLayout::GetControlType() const - { - return control_type; - } - - Size FrameLayout::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - auto max_child_size = Size::Zero(); - for (auto control: GetChildren()) - { - control->Measure(available_size, additional_info); - const auto&& size = control->GetDesiredSize(); - if (max_child_size.width < size.width) - max_child_size.width = size.width; - if (max_child_size.height < size.height) - max_child_size.height = size.height; - } - - // coerce size fro stretch. - for (auto control: GetChildren()) - { - auto size = control->GetDesiredSize(); - const auto layout_params = control->GetLayoutParams(); - if (layout_params->width.mode == MeasureMode::Stretch) - size.width = max_child_size.width; - if (layout_params->height.mode == MeasureMode::Stretch) - size.height = max_child_size.height; - control->SetDesiredSize(size); - } - - return max_child_size; - } - - void FrameLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - for (auto control: GetChildren()) - { - const auto layout_params = control->GetLayoutParams(); - const auto size = control->GetDesiredSize(); - - auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float - { - switch (alignment) - { - case Alignment::Center: - return anchor + (layout_length - control_length) / 2; - case Alignment::Start: - return anchor; - case Alignment::End: - return anchor + layout_length - control_length; - default: - UnreachableCode(); - } - }; - - control->Layout(Rect(Point( - calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width), - calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height) - ), size), additional_info); - } - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\frame_layout.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\linear_layout.cpp -//-------------------------------------------------------- - -#include <algorithm> - - -namespace cru::ui::controls -{ - LinearLayout::LinearLayout(const Orientation orientation) - : orientation_(orientation) - { - - } - - StringView LinearLayout::GetControlType() const - { - return control_type; - } - - Size LinearLayout::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - auto actual_size_for_children = Size::Zero(); - - float secondary_side_child_max_length = 0; - - std::list<Control*> stretch_control_list; - - // First measure Content and Exactly and count Stretch. - if (orientation_ == Orientation::Horizontal) - for(auto control: GetInternalChildren()) - { - const auto mode = control->GetLayoutParams()->width.mode; - if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) - { - Size current_available_size(AtLeast0(available_size.width - actual_size_for_children.width), available_size.height); - control->Measure(current_available_size, additional_info); - const auto size = control->GetDesiredSize(); - actual_size_for_children.width += size.width; - secondary_side_child_max_length = std::max(size.height, secondary_side_child_max_length); - } - else - stretch_control_list.push_back(control); - } - else - for(auto control: GetInternalChildren()) - { - const auto mode = control->GetLayoutParams()->height.mode; - if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) - { - Size current_available_size(available_size.width, AtLeast0(available_size.height - actual_size_for_children.height)); - control->Measure(current_available_size, additional_info); - const auto size = control->GetDesiredSize(); - actual_size_for_children.height += size.height; - secondary_side_child_max_length = std::max(size.width, secondary_side_child_max_length); - } - else - stretch_control_list.push_back(control); - } - - if (orientation_ == Orientation::Horizontal) - { - const auto available_width = AtLeast0(available_size.width - actual_size_for_children.width) / stretch_control_list.size(); - for (const auto control : stretch_control_list) - { - control->Measure(Size(available_width, available_size.height), additional_info); - const auto size = control->GetDesiredSize(); - actual_size_for_children.width += size.width; - secondary_side_child_max_length = std::max(size.height, secondary_side_child_max_length); - } - } - else - { - const auto available_height = AtLeast0(available_size.height - actual_size_for_children.height) / stretch_control_list.size(); - for (const auto control : stretch_control_list) - { - control->Measure(Size(available_size.width, available_height), additional_info); - const auto size = control->GetDesiredSize(); - actual_size_for_children.height += size.height; - secondary_side_child_max_length = std::max(size.width, secondary_side_child_max_length); - } - } - - if (orientation_ == Orientation::Horizontal) - { - for (auto control : GetInternalChildren()) - { - if (control->GetLayoutParams()->height.mode == MeasureMode::Stretch) - { - control->SetDesiredSize(Size(control->GetDesiredSize().width, secondary_side_child_max_length)); - } - } - actual_size_for_children.height = secondary_side_child_max_length; - } - else - { - for (auto control : GetInternalChildren()) - { - if (control->GetLayoutParams()->width.mode == MeasureMode::Stretch) - { - control->SetDesiredSize(Size(secondary_side_child_max_length, control->GetDesiredSize().height)); - } - } - - actual_size_for_children.width = secondary_side_child_max_length; - } - - return actual_size_for_children; - } - - void LinearLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - float current_main_side_anchor = 0; - for(auto control: GetInternalChildren()) - { - const auto layout_params = control->GetLayoutParams(); - const auto size = control->GetDesiredSize(); - const auto alignment = orientation_ == Orientation::Horizontal ? layout_params->height.alignment : layout_params->width.alignment; - - auto&& calculate_secondary_side_anchor = [alignment](const float layout_length, const float control_length) -> float - { - switch (alignment) - { - case Alignment::Center: - return (layout_length - control_length) / 2; - case Alignment::Start: - return 0; - case Alignment::End: - return layout_length - control_length; - default: - UnreachableCode(); - } - }; - - auto&& calculate_rect = [rect, size](const float anchor_left, const float anchor_top) - { - return Rect(Point(rect.left + anchor_left, rect.top + anchor_top), size); - }; - - if (orientation_ == Orientation::Horizontal) - { - control->Layout(calculate_rect(current_main_side_anchor, calculate_secondary_side_anchor(rect.height, size.height)), additional_info); - current_main_side_anchor += size.width; - } - else - { - control->Layout(calculate_rect(calculate_secondary_side_anchor(rect.width, size.width), current_main_side_anchor), additional_info); - current_main_side_anchor += size.height; - } - } - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\linear_layout.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\list_item.cpp -//-------------------------------------------------------- - - -namespace cru::ui::controls -{ - ListItem::ListItem() - { - const auto predefine_resources = UiManager::GetInstance()->GetPredefineResources(); - - brushes_[State::Normal].border_brush = predefine_resources->list_item_normal_border_brush; - brushes_[State::Normal].fill_brush = predefine_resources->list_item_normal_fill_brush; - brushes_[State::Hover] .border_brush = predefine_resources->list_item_hover_border_brush; - brushes_[State::Hover] .fill_brush = predefine_resources->list_item_hover_fill_brush; - brushes_[State::Select].border_brush = predefine_resources->list_item_select_border_brush; - brushes_[State::Select].fill_brush = predefine_resources->list_item_select_fill_brush; - - draw_foreground_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - const auto rect = Rect(Point::Zero(), GetRect(RectRange::Padding).GetSize()); - device_context->FillRectangle(Convert(rect), brushes_[state_].fill_brush.Get()); - device_context->DrawRectangle(Convert(rect.Shrink(Thickness(0.5))), brushes_[state_].border_brush.Get(), 1); - }); - - mouse_enter_event.direct.AddHandler([this](events::MouseEventArgs& args) - { - if (GetState() == State::Select) - return; - - if (IsAnyMouseButtonDown()) - return; - - SetState(State::Hover); - }); - - mouse_leave_event.direct.AddHandler([this](events::MouseEventArgs& args) - { - if (GetState() == State::Select) - return; - - SetState(State::Normal); - }); - - mouse_click_event.direct.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - SetState(State::Select); - }); - } - - StringView ListItem::GetControlType() const - { - return control_type; - } - - void ListItem::SetState(const State state) - { - state_ = state; - InvalidateDraw(); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\list_item.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\popup_menu.cpp -//-------------------------------------------------------- - - -namespace cru::ui::controls -{ - Window* CreatePopupMenu(const Point& anchor, const std::vector<MenuItemInfo>& items, Window* parent) - { - const auto popup = Window::CreatePopup(parent); - - popup->lose_focus_event.bubble.AddHandler([popup](events::FocusChangeEventArgs& args) - { - if (args.IsWindow()) - popup->Close(); - }); - - const auto create_menu_item = [popup](const String& text, const std::function<void()>& action) -> ListItem* - { - auto text_block = TextBlock::Create(text); - text_block->GetLayoutParams()->width.alignment = Alignment::Start; - - auto list_item = CreateWithLayout<ListItem>( - LayoutSideParams::Stretch(Alignment::Center), - LayoutSideParams::Content(Alignment::Start), - text_block - ); - - list_item->mouse_click_event.bubble.AddHandler([popup, action](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - { - action(); - popup->Close(); - } - }); - - return list_item; - }; - - const auto menu = LinearLayout::Create(LinearLayout::Orientation::Vertical); - - menu->SetBordered(true); - - for (const auto& item : items) - menu->AddChild(create_menu_item(item.first, item.second)); - - popup->SetChild(menu); - - popup->SetSizeFitContent(); - popup->SetWindowPosition(anchor); - - return popup; - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\popup_menu.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\scroll_control.cpp -//-------------------------------------------------------- - -#include <limits> - - -namespace cru::ui::controls -{ - constexpr auto scroll_bar_width = 15.0f; - - ScrollControl::ScrollControl(const bool container) - { - SetClipContent(true); - - draw_foreground_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - const auto predefined = UiManager::GetInstance()->GetPredefineResources(); - - if (is_horizontal_scroll_bar_visible_) - { - device_context->FillRectangle( - Convert(horizontal_bar_info_.border), - predefined->scroll_bar_background_brush.Get() - ); - - device_context->FillRectangle( - Convert(horizontal_bar_info_.bar), - predefined->scroll_bar_brush.Get() - ); - - device_context->DrawLine( - Convert(horizontal_bar_info_.border.GetLeftTop()), - Convert(horizontal_bar_info_.border.GetRightTop()), - predefined->scroll_bar_border_brush.Get() - ); - } - - if (is_vertical_scroll_bar_visible_) - { - device_context->FillRectangle( - Convert(vertical_bar_info_.border), - predefined->scroll_bar_background_brush.Get() - ); - - device_context->FillRectangle( - Convert(vertical_bar_info_.bar), - predefined->scroll_bar_brush.Get() - ); - - device_context->DrawLine( - Convert(vertical_bar_info_.border.GetLeftTop()), - Convert(vertical_bar_info_.border.GetLeftBottom()), - predefined->scroll_bar_border_brush.Get() - ); - } - }); - - mouse_down_event.tunnel.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - { - const auto point = args.GetPoint(this); - if (is_vertical_scroll_bar_visible_ && vertical_bar_info_.bar.IsPointInside(point)) - { - GetWindow()->CaptureMouseFor(this); - is_pressing_scroll_bar_ = Orientation::Vertical; - pressing_delta_ = point.y - vertical_bar_info_.bar.top; - args.SetHandled(); - return; - } - - if (is_horizontal_scroll_bar_visible_ && horizontal_bar_info_.bar.IsPointInside(point)) - { - GetWindow()->CaptureMouseFor(this); - pressing_delta_ = point.x - horizontal_bar_info_.bar.left; - is_pressing_scroll_bar_ = Orientation::Horizontal; - args.SetHandled(); - return; - } - } - }); - - mouse_move_event.tunnel.AddHandler([this](events::MouseEventArgs& args) - { - const auto mouse_point = args.GetPoint(this); - - if (is_pressing_scroll_bar_ == Orientation::Horizontal) - { - const auto new_head_position = mouse_point.x - pressing_delta_; - const auto new_offset = new_head_position / horizontal_bar_info_.border.width * view_width_; - SetScrollOffset(new_offset, std::nullopt); - args.SetHandled(); - return; - } - - if (is_pressing_scroll_bar_ == Orientation::Vertical) - { - const auto new_head_position = mouse_point.y - pressing_delta_; - const auto new_offset = new_head_position / vertical_bar_info_.border.height * view_height_; - SetScrollOffset(std::nullopt, new_offset); - args.SetHandled(); - return; - } - }); - - mouse_up_event.tunnel.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left && is_pressing_scroll_bar_.has_value()) - { - GetWindow()->ReleaseCurrentMouseCapture(); - is_pressing_scroll_bar_ = std::nullopt; - args.SetHandled(); - } - }); - - mouse_wheel_event.bubble.AddHandler([this](events::MouseWheelEventArgs& args) - { - constexpr const auto view_delta = 30.0f; - - if (args.GetDelta() == 0.0f) - return; - - const auto content_rect = GetRect(RectRange::Content); - if (IsVerticalScrollEnabled() && GetScrollOffsetY() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewHeight() - content_rect.height))) - { - SetScrollOffset(std::nullopt, GetScrollOffsetY() - args.GetDelta() / WHEEL_DELTA * view_delta); - args.SetHandled(); - return; - } - - if (IsHorizontalScrollEnabled() && GetScrollOffsetX() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewWidth() - content_rect.width))) - { - SetScrollOffset(GetScrollOffsetX() - args.GetDelta() / WHEEL_DELTA * view_delta, std::nullopt); - args.SetHandled(); - return; - } - }); - } - - ScrollControl::~ScrollControl() - { - - } - - StringView ScrollControl::GetControlType() const - { - return control_type; - } - - void ScrollControl::SetHorizontalScrollEnabled(const bool enable) - { - horizontal_scroll_enabled_ = enable; - InvalidateLayout(); - InvalidateDraw(); - } - - void ScrollControl::SetVerticalScrollEnabled(const bool enable) - { - vertical_scroll_enabled_ = enable; - InvalidateLayout(); - InvalidateDraw(); - } - - void ScrollControl::SetHorizontalScrollBarVisibility(const ScrollBarVisibility visibility) - { - if (visibility != horizontal_scroll_bar_visibility_) - { - horizontal_scroll_bar_visibility_ = visibility; - switch (visibility) - { - case ScrollBarVisibility::Always: - is_horizontal_scroll_bar_visible_ = true; - break; - case ScrollBarVisibility::None: - is_horizontal_scroll_bar_visible_ = false; - break; - case ScrollBarVisibility::Auto: - UpdateScrollBarVisibility(); - } - InvalidateDraw(); - } - } - - void ScrollControl::SetVerticalScrollBarVisibility(const ScrollBarVisibility visibility) - { - if (visibility != vertical_scroll_bar_visibility_) - { - vertical_scroll_bar_visibility_ = visibility; - switch (visibility) - { - case ScrollBarVisibility::Always: - is_vertical_scroll_bar_visible_ = true; - break; - case ScrollBarVisibility::None: - is_vertical_scroll_bar_visible_ = false; - break; - case ScrollBarVisibility::Auto: - UpdateScrollBarVisibility(); - } - InvalidateDraw(); - } - - } - - void ScrollControl::SetScrollOffset(std::optional<float> x, std::optional<float> y) - { - CoerceAndSetOffsets(x.value_or(GetScrollOffsetX()), y.value_or(GetScrollOffsetY())); - } - - void ScrollControl::SetViewWidth(const float length) - { - view_width_ = length; - } - - void ScrollControl::SetViewHeight(const float length) - { - view_height_ = length; - } - - Size ScrollControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - const auto layout_params = GetLayoutParams(); - - auto available_size_for_children = available_size; - if (IsHorizontalScrollEnabled()) - { - if (layout_params->width.mode == MeasureMode::Content) - debug::DebugMessage(L"ScrollControl: Width measure mode is Content and horizontal scroll is enabled. So Stretch is used instead."); - - available_size_for_children.width = std::numeric_limits<float>::max(); - } - - if (IsVerticalScrollEnabled()) - { - if (layout_params->height.mode == MeasureMode::Content) - debug::DebugMessage(L"ScrollControl: Height measure mode is Content and vertical scroll is enabled. So Stretch is used instead."); - - available_size_for_children.height = std::numeric_limits<float>::max(); - } - - const auto child = GetChild(); - - auto size = Size::Zero(); - if (child) - { - child->Measure(available_size_for_children, AdditionalMeasureInfo{false, false}); - size = child->GetDesiredSize(); - } - - - auto result = size; - if (IsHorizontalScrollEnabled()) - { - SetViewWidth(size.width); - result.width = available_size.width; - } - if (IsVerticalScrollEnabled()) - { - SetViewHeight(size.height); - result.height = available_size.height; - } - - return result; - } - - void ScrollControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - auto layout_rect = rect; - - if (IsHorizontalScrollEnabled()) - layout_rect.width = GetViewWidth(); - if (IsVerticalScrollEnabled()) - layout_rect.height = GetViewHeight(); - - const auto child = GetChild(); - - if (child) - { - const auto layout_params = child->GetLayoutParams(); - const auto size = child->GetDesiredSize(); - - auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float - { - switch (alignment) - { - case Alignment::Center: - return anchor + (layout_length - control_length) / 2; - case Alignment::Start: - return anchor; - case Alignment::End: - return anchor + layout_length - control_length; - default: - UnreachableCode(); - } - }; - - child->Layout(Rect(Point( - IsHorizontalScrollEnabled() ? layout_rect.left + offset_x_ : calculate_anchor(layout_rect.left, layout_params->width.alignment, layout_rect.width, size.width), - IsVerticalScrollEnabled() ? layout_rect.top + offset_y_ : calculate_anchor(layout_rect.top, layout_params->height.alignment, layout_rect.height, size.height) - ), size), additional_info); - } - } - - void ScrollControl::OnRectChange(const Rect& old_rect, const Rect& new_rect) - { - UpdateScrollBarBorderInfo(); - CoerceAndSetOffsets(offset_x_, offset_y_, false); - UpdateScrollBarVisibility(); - } - - void ScrollControl::CoerceAndSetOffsets(const float offset_x, const float offset_y, const bool update_children) - { - const auto old_offset_x = offset_x_; - const auto old_offset_y = offset_y_; - - const auto content_rect = GetRect(RectRange::Content); - offset_x_ = Coerce(offset_x, 0.0f, AtLeast0(view_width_ - content_rect.width)); - offset_y_ = Coerce(offset_y, 0.0f, AtLeast0(view_height_ - content_rect.height)); - UpdateScrollBarBarInfo(); - - if (update_children) - { - if (const auto child = GetChild()) - { - const auto old_position = child->GetOffset(); - child->SetRect(Rect(Point( - old_position.x + old_offset_x - offset_x_, - old_position.y + old_offset_y - offset_y_ - ), child->GetSize())); - child->RefreshDescendantPositionCache(); - } - } - InvalidateDraw(); - } - - void ScrollControl::UpdateScrollBarVisibility() - { - const auto content_rect = GetRect(RectRange::Content); - if (GetHorizontalScrollBarVisibility() == ScrollBarVisibility::Auto) - is_horizontal_scroll_bar_visible_ = view_width_ > content_rect.width; - if (GetVerticalScrollBarVisibility() == ScrollBarVisibility::Auto) - is_vertical_scroll_bar_visible_ = view_height_ > content_rect.height; - } - - void ScrollControl::UpdateScrollBarBorderInfo() - { - const auto content_rect = GetRect(RectRange::Content); - horizontal_bar_info_.border = Rect(content_rect.left, content_rect.GetBottom() - scroll_bar_width, content_rect.width, scroll_bar_width); - vertical_bar_info_.border = Rect(content_rect.GetRight() - scroll_bar_width , content_rect.top, scroll_bar_width, content_rect.height); - } - - void ScrollControl::UpdateScrollBarBarInfo() - { - const auto content_rect = GetRect(RectRange::Content); - { - const auto& border = horizontal_bar_info_.border; - if (view_width_ <= content_rect.width) - horizontal_bar_info_.bar = border; - else - { - const auto bar_length = border.width * content_rect.width / view_width_; - const auto offset = border.width * offset_x_ / view_width_; - horizontal_bar_info_.bar = Rect(border.left + offset, border.top, bar_length, border.height); - } - } - { - const auto& border = vertical_bar_info_.border; - if (view_height_ <= content_rect.height) - vertical_bar_info_.bar = border; - else - { - const auto bar_length = border.height * content_rect.height / view_height_; - const auto offset = border.height * offset_y_ / view_height_; - vertical_bar_info_.bar = Rect(border.left, border.top + offset, border.width, bar_length); - } - } - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\scroll_control.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\text_block.cpp -//-------------------------------------------------------- - - -namespace cru::ui::controls -{ - TextBlock::TextBlock() : TextControl( - UiManager::GetInstance()->GetPredefineResources()->text_block_text_format, - UiManager::GetInstance()->GetPredefineResources()->text_block_text_brush - ) - { - - } - - StringView TextBlock::GetControlType() const - { - return control_type; - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\text_block.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\text_box.cpp -//-------------------------------------------------------- - -#include <cwctype> -#include <cassert> - - -namespace cru::ui::controls -{ - TextBox::TextBox() : TextControl( - UiManager::GetInstance()->GetPredefineResources()->text_box_text_format, - UiManager::GetInstance()->GetPredefineResources()->text_box_text_brush - ) - { - SetSelectable(true); - - caret_brush_ = UiManager::GetInstance()->GetPredefineResources()->text_box_caret_brush; - - GetBorderProperty() = UiManager::GetInstance()->GetPredefineResources()->text_box_border; - SetBordered(true); - - draw_content_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - if (is_caret_show_) - { - const auto caret_half_width = UiManager::GetInstance()->GetCaretInfo().half_caret_width; - FLOAT x, y; - DWRITE_HIT_TEST_METRICS metrics{}; - ThrowIfFailed(text_layout_->HitTestTextPosition(caret_position_, FALSE, &x, &y, &metrics)); - device_context->FillRectangle(D2D1::RectF(metrics.left - caret_half_width, metrics.top, metrics.left + caret_half_width, metrics.top + metrics.height), caret_brush_.Get()); - } - }); - - get_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args) - { - assert(!caret_timer_.has_value()); - is_caret_show_ = true; - caret_timer_ = SetInterval(UiManager::GetInstance()->GetCaretInfo().caret_blink_duration, [this] - { - is_caret_show_ = !is_caret_show_; - InvalidateDraw(); - }); - }); - - lose_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args) - { - assert(caret_timer_.has_value()); - caret_timer_->Cancel(); - caret_timer_ = std::nullopt; - is_caret_show_ = false; - }); - - key_down_event.bubble.AddHandler([this](events::KeyEventArgs& args) - { - if (args.GetVirtualCode() == VK_LEFT && caret_position_ > 0) - { - if (IsKeyDown(VK_SHIFT)) - { - if (GetCaretSelectionSide()) - ShiftLeftSelectionRange(-1); - else - ShiftRightSelectionRange(-1); - } - else - { - const auto selection = GetSelectedRange(); - if (selection.has_value()) - { - ClearSelection(); - caret_position_ = selection.value().position; - } - else - caret_position_--; - } - InvalidateDraw(); - } - - if (args.GetVirtualCode() == VK_RIGHT && caret_position_ < GetText().size()) - { - if (IsKeyDown(VK_SHIFT)) - { - if (GetCaretSelectionSide()) - ShiftLeftSelectionRange(1); - else - ShiftRightSelectionRange(1); - } - else - { - const auto selection = GetSelectedRange(); - if (selection.has_value()) - { - ClearSelection(); - caret_position_ = selection.value().position + selection.value().count; - } - else - caret_position_++; - } - } - }); - - char_event.bubble.AddHandler([this](events::CharEventArgs& args) - { - if (args.GetChar() == L'\b') - { - if (GetSelectedRange().has_value()) - { - const auto selection_range = GetSelectedRange().value(); - auto text = GetText(); - text.erase(text.cbegin() + selection_range.position, text.cbegin() + selection_range.position + selection_range.count); - SetText(text); - caret_position_ = selection_range.position; - ClearSelection(); - } - else - { - if (caret_position_ > 0) - { - auto text = GetText(); - if (!text.empty()) - { - const auto position = --caret_position_; - text.erase(text.cbegin() + position); - SetText(text); - } - } - } - return; - } - - if (std::iswprint(args.GetChar())) - { - if (GetSelectedRange().has_value()) - { - const auto selection_range = GetSelectedRange().value(); - auto text = GetText(); - text.erase(selection_range.position, selection_range.count); - text.insert(text.cbegin() + selection_range.position, args.GetChar()); - SetText(text); - caret_position_ = selection_range.position + 1; - ClearSelection(); - } - else - { - ClearSelection(); - const auto position = caret_position_++; - auto text = GetText(); - text.insert(text.cbegin() + position, { args.GetChar() }); - SetText(text); - } - } - }); - } - - TextBox::~TextBox() = default; - - StringView TextBox::GetControlType() const - { - return control_type; - } - - void TextBox::RequestChangeCaretPosition(const unsigned position) - { - caret_position_ = position; - InvalidateDraw(); - } - - bool TextBox::GetCaretSelectionSide() const - { - const auto selection = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); - if (selection.first == caret_position_) - return true; - if (selection.second == caret_position_) - return false; - assert(false); - return true; - } - - void TextBox::ShiftLeftSelectionRange(const int count) - { - const auto selection_range_side = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); - int new_left = selection_range_side.first + count; - new_left = new_left < 0 ? 0 : new_left; // at least 0 - caret_position_ = new_left; - SetSelectedRange(TextRange::FromTwoSides(static_cast<unsigned>(new_left), selection_range_side.second)); - } - - void TextBox::ShiftRightSelectionRange(const int count) - { - const auto selection_range_side = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); - int new_right = selection_range_side.second + count; - new_right = new_right < 0 ? 0 : new_right; // at least 0 - caret_position_ = new_right; - SetSelectedRange(TextRange::FromTwoSides(selection_range_side.first, static_cast<unsigned>(new_right))); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\text_box.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\text_control.cpp -//-------------------------------------------------------- - -#include <cassert> - - -namespace cru::ui::controls -{ - namespace - { - unsigned TextLayoutHitTest(IDWriteTextLayout* text_layout, const Point& point) - { - BOOL is_trailing, is_inside; - DWRITE_HIT_TEST_METRICS metrics{}; - text_layout->HitTestPoint(point.x, point.y, &is_trailing, &is_inside, &metrics); - return is_trailing == 0 ? metrics.textPosition : metrics.textPosition + 1; - } - - void DrawSelectionRect(ID2D1DeviceContext* device_context, IDWriteTextLayout* layout, ID2D1Brush* brush, const std::optional<TextRange> range) - { - if (range.has_value()) - { - DWRITE_TEXT_METRICS text_metrics{}; - ThrowIfFailed(layout->GetMetrics(&text_metrics)); - const auto metrics_count = text_metrics.lineCount * text_metrics.maxBidiReorderingDepth; - - std::vector<DWRITE_HIT_TEST_METRICS> hit_test_metrics(metrics_count); - UINT32 actual_count; - layout->HitTestTextRange( - range.value().position, range.value().count, - 0, 0, - hit_test_metrics.data(), metrics_count, &actual_count - ); - - hit_test_metrics.erase(hit_test_metrics.cbegin() + actual_count, hit_test_metrics.cend()); - - for (const auto& metrics : hit_test_metrics) - device_context->FillRoundedRectangle(D2D1::RoundedRect(D2D1::RectF(metrics.left, metrics.top, metrics.left + metrics.width, metrics.top + metrics.height), 3, 3), brush); - } - } - } - - TextControl::TextControl(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format, - const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush) - { - text_format_ = init_text_format; - - RecreateTextLayout(); - - brush_ = init_brush; - - selection_brush_ = UiManager::GetInstance()->GetPredefineResources()->text_control_selection_brush; - - SetClipContent(true); - - draw_content_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - DrawSelectionRect(device_context, text_layout_.Get(), selection_brush_.Get(), selected_range_); - device_context->DrawTextLayout(D2D1::Point2F(), text_layout_.Get(), brush_.Get()); - }); - - mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (is_selectable_ && args.GetMouseButton() == MouseButton::Left && GetRect(RectRange::Padding).IsPointInside(args.GetPoint(this, RectRange::Margin))) - { - selected_range_ = std::nullopt; - const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this)); - RequestChangeCaretPosition(hit_test_result); - mouse_down_position_ = hit_test_result; - is_selecting_ = true; - GetWindow()->CaptureMouseFor(this); - InvalidateDraw(); - } - }); - - mouse_move_event.bubble.AddHandler([this](events::MouseEventArgs& args) - { - if (is_selecting_) - { - const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this)); - RequestChangeCaretPosition(hit_test_result); - selected_range_ = TextRange::FromTwoSides(hit_test_result, mouse_down_position_); - InvalidateDraw(); - } - UpdateCursor(args.GetPoint(this, RectRange::Margin)); - }); - - - mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - { - if (is_selecting_) - { - is_selecting_ = false; - GetWindow()->ReleaseCurrentMouseCapture(); - } - } - }); - - lose_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args) - { - if (is_selecting_) - { - is_selecting_ = false; - GetWindow()->ReleaseCurrentMouseCapture(); - } - if (!args.IsWindow()) // If the focus lose is triggered window-wide, then save the selection state. Otherwise, clear selection. - { - selected_range_ = std::nullopt; - InvalidateDraw(); - } - }); - } - - - void TextControl::SetText(const String& text) - { - if (text_ != text) - { - const auto old_text = text_; - text_ = text; - OnTextChangedCore(old_text, text); - } - } - - void TextControl::SetBrush(const Microsoft::WRL::ComPtr<ID2D1Brush>& brush) - { - brush_ = brush; - InvalidateDraw(); - } - - void TextControl::SetTextFormat(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& text_format) - { - text_format_ = text_format; - RecreateTextLayout(); - InvalidateDraw(); - } - - void TextControl::SetSelectable(const bool is_selectable) - { - if (is_selectable_ != is_selectable) - { - if (!is_selectable) - { - if (is_selecting_) - { - is_selecting_ = false; - GetWindow()->ReleaseCurrentMouseCapture(); - } - selected_range_ = std::nullopt; - InvalidateDraw(); - } - is_selectable_ = is_selectable; - UpdateCursor(std::nullopt); - } - } - - void TextControl::SetSelectedRange(std::optional<TextRange> text_range) - { - if (is_selectable_) - { - selected_range_ = text_range; - InvalidateDraw(); - } - } - - Size TextControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo&) - { - ThrowIfFailed(text_layout_->SetMaxWidth(available_size.width)); - ThrowIfFailed(text_layout_->SetMaxHeight(available_size.height)); - - DWRITE_TEXT_METRICS metrics{}; - - ThrowIfFailed(text_layout_->GetMetrics(&metrics)); - - const Size measure_result(metrics.width, metrics.height); - - return measure_result; - } - - void TextControl::RequestChangeCaretPosition(unsigned position) - { - - } - - void TextControl::OnRectChange(const Rect& old_rect, const Rect& new_rect) - { - const auto content = GetRect(RectRange::Content); - ThrowIfFailed(text_layout_->SetMaxWidth(content.width)); - ThrowIfFailed(text_layout_->SetMaxHeight(content.height)); - } - - void TextControl::OnTextChangedCore(const String& old_text, const String& new_text) - { - RecreateTextLayout(); - InvalidateLayout(); - InvalidateDraw(); - } - - void TextControl::RecreateTextLayout() - { - assert(text_format_ != nullptr); - - text_layout_ = nullptr; - - const auto dwrite_factory = graph::GraphManager::GetInstance()->GetDWriteFactory(); - - const auto&& size = GetSize(); - - ThrowIfFailed(dwrite_factory->CreateTextLayout( - text_.c_str(), static_cast<UINT32>(text_.size()), - text_format_.Get(), - size.width, size.height, - &text_layout_ - )); - } - - void TextControl::UpdateCursor(const std::optional<Point>& point) - { - if (!is_selectable_) - { - SetCursor(nullptr); - return; - } - - const auto window = GetWindow(); - if (window == nullptr) - { - SetCursor(nullptr); - return; - } - - if (is_selecting_) - { - SetCursor(cursors::i_beam); - return; - } - - const auto p = point.value_or(WindowToControl(window->GetMousePosition())); - if (GetRect(RectRange::Padding).IsPointInside(p)) - SetCursor(cursors::i_beam); - else - SetCursor(nullptr); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\text_control.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\toggle_button.cpp -//-------------------------------------------------------- - - -namespace cru::ui::controls -{ - using animations::AnimationBuilder; - - // ui length parameters of toggle button. - constexpr float half_height = 15; - constexpr float half_width = half_height * 2; - constexpr float stroke_width = 3; - constexpr float inner_circle_radius = half_height - stroke_width; - constexpr float inner_circle_x = half_width - half_height; - - ToggleButton::ToggleButton() : current_circle_position_(-inner_circle_x) - { - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(D2D1::RoundedRect(D2D1::RectF(-half_width, -half_height, half_width, half_height), half_height, half_height), &frame_path_); - - on_brush_ = UiManager::GetInstance()->GetPredefineResources()->toggle_button_on_brush; - off_brush_ = UiManager::GetInstance()->GetPredefineResources()->toggle_button_off_brush; - - draw_content_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - const auto size = GetSize(); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2), [this](ID2D1DeviceContext* device_context) - { - if (state_) - { - device_context->DrawGeometry(frame_path_.Get(), on_brush_.Get(), stroke_width); - device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), on_brush_.Get()); - } - else - { - device_context->DrawGeometry(frame_path_.Get(), off_brush_.Get(), stroke_width); - device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), off_brush_.Get()); - } - }); - }); - - mouse_click_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - Toggle(); - }); - } - - - StringView ToggleButton::GetControlType() const - { - return control_type; - } - - bool ToggleButton::IsPointInside(const Point& point) - { - const auto size = GetSize(); - const auto transform = D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2); - BOOL contains; - frame_path_->FillContainsPoint(Convert(point), transform, &contains); - if (!contains) - frame_path_->StrokeContainsPoint(Convert(point), stroke_width, nullptr, transform, &contains); - return contains != 0; - } - - void ToggleButton::SetState(const bool state) - { - if (state != state_) - { - state_ = state; - float destination_x; - - if (state) - destination_x = inner_circle_x; - else - destination_x = -inner_circle_x; - - const auto previous_position = current_circle_position_; - const auto delta = destination_x - current_circle_position_; - - constexpr auto total_time = FloatSecond(0.2); - - const auto time = total_time * (std::abs(delta) / (inner_circle_x * 2)); - - // ReSharper disable once CppExpressionWithoutSideEffects - AnimationBuilder(Format(L"ToggleButton {}", reinterpret_cast<size_t>(this)), time) - .AddStepHandler([=](auto, const double percentage) - { - current_circle_position_ = static_cast<float>(previous_position + delta * percentage); - InvalidateDraw(); - }) - .Start(); - - events::ToggleEventArgs args(this, this, state); - toggle_event.Raise(args); - InvalidateDraw(); - } - } - - void ToggleButton::Toggle() - { - SetState(!GetState()); - } - - Size ToggleButton::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo&) - { - const Size result_size( - half_width * 2 + stroke_width, - half_height * 2 + stroke_width - ); - - return result_size; - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\toggle_button.cpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\events\ui_event.cpp -//-------------------------------------------------------- - - -namespace cru::ui::events -{ - Point MouseEventArgs::GetPoint(Control* control, const RectRange range) const - { - if (point_.has_value()) - return control->TransformPoint(control->WindowToControl(point_.value()), RectRange::Margin, range); - return Point(); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\events\ui_event.cpp -//-------------------------------------------------------- diff --git a/CruUI-Generate/cru_ui.hpp b/CruUI-Generate/cru_ui.hpp deleted file mode 100644 index 11222e6a..00000000 --- a/CruUI-Generate/cru_ui.hpp +++ /dev/null @@ -1,3514 +0,0 @@ -#pragma once -//-------------------------------------------------------- -//-------begin of file: src\any_map.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective -//-------------------------------------------------------- -//-------begin of file: src\pre.hpp -//-------------------------------------------------------- - -#ifdef _DEBUG -#define CRU_DEBUG -#endif - -#ifdef CRU_DEBUG -#define CRU_DEBUG_LAYOUT -#endif - - -#ifdef CRU_DEBUG -// ReSharper disable once IdentifierTypo -// ReSharper disable once CppInconsistentNaming -#define _CRTDBG_MAP_ALLOC -#include <cstdlib> -#include <crtdbg.h> -#endif -//-------------------------------------------------------- -//-------end of file: src\pre.hpp -//-------------------------------------------------------- - -#include <any> -#include <unordered_map> -#include <functional> -#include <optional> -#include <typeinfo> - -//-------------------------------------------------------- -//-------begin of file: src\base.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <string> -#include <stdexcept> -#include <string_view> -#include <chrono> - -namespace cru -{ - template<typename T> struct type_tag {}; - - //typedefs - using String = std::wstring; - using MultiByteString = std::string; - - using StringView = std::wstring_view; - using MultiByteStringView = std::string_view; - - using FloatSecond = std::chrono::duration<double, std::chrono::seconds::period>; - - enum class FlowControl - { - Continue, - Break - }; - - - class Object - { - public: - Object() = default; - Object(const Object&) = default; - Object& operator = (const Object&) = default; - Object(Object&&) = default; - Object& operator = (Object&&) = default; - virtual ~Object() = default; - }; - - struct Interface - { - virtual ~Interface() = default; - }; - - [[noreturn]] inline void UnreachableCode() - { - throw std::logic_error("Unreachable code."); - } - - MultiByteString ToUtf8String(const StringView& string); - - inline void Require(const bool condition, const MultiByteStringView& error_message) - { - if (!condition) - throw std::invalid_argument(error_message.data()); - } -} -//-------------------------------------------------------- -//-------end of file: src\base.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\format.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - -namespace cru -{ - namespace details - { - constexpr StringView PlaceHolder(type_tag<String>) - { - return StringView(L"{}"); - } - - constexpr MultiByteStringView PlaceHolder(type_tag<MultiByteString>) - { - return MultiByteStringView("{}"); - } - - template<typename TString> - void FormatInternal(TString& string) - { - const auto find_result = string.find(PlaceHolder(type_tag<TString>{})); - if (find_result != TString::npos) - throw std::invalid_argument("There is more placeholders than args."); - } - - template<typename TString, typename T, typename... TRest> - void FormatInternal(TString& string, const T& arg, const TRest&... args) - { - const auto find_result = string.find(PlaceHolder(type_tag<TString>{})); - if (find_result == TString::npos) - throw std::invalid_argument("There is less placeholders than args."); - - string.replace(find_result, 2, FormatToString(arg, type_tag<TString>{})); - FormatInternal<TString>(string, args...); - } - } - - template<typename... T> - String Format(const StringView& format, const T&... args) - { - String result(format); - details::FormatInternal<String>(result, args...); - return result; - } - - template<typename... T> - MultiByteString Format(const MultiByteStringView& format, const T&... args) - { - MultiByteString result(format); - details::FormatInternal<MultiByteString>(result, args...); - return result; - } - -#define CRU_FORMAT_NUMBER(type) \ - inline String FormatToString(const type number, type_tag<String>) \ - { \ - return std::to_wstring(number); \ - } \ - inline MultiByteString FormatToString(const type number, type_tag<MultiByteString>) \ - { \ - return std::to_string(number); \ - } - - CRU_FORMAT_NUMBER(int) - CRU_FORMAT_NUMBER(short) - CRU_FORMAT_NUMBER(long) - CRU_FORMAT_NUMBER(long long) - CRU_FORMAT_NUMBER(unsigned int) - CRU_FORMAT_NUMBER(unsigned short) - CRU_FORMAT_NUMBER(unsigned long) - CRU_FORMAT_NUMBER(unsigned long long) - CRU_FORMAT_NUMBER(float) - CRU_FORMAT_NUMBER(double) - -#undef CRU_FORMAT_NUMBER - - inline StringView FormatToString(const String& string, type_tag<String>) - { - return string; - } - - inline MultiByteString FormatToString(const MultiByteString& string, type_tag<MultiByteString>) - { - return string; - } - - inline StringView FormatToString(const StringView& string, type_tag<String>) - { - return string; - } - - inline MultiByteStringView FormatToString(const MultiByteStringView& string, type_tag<MultiByteString>) - { - return string; - } - - inline StringView FormatToString(const wchar_t* string, type_tag<String>) - { - return StringView(string); - } - - inline MultiByteStringView FormatToString(const char* string, type_tag<MultiByteString>) - { - return MultiByteString(string); - } -} -//-------------------------------------------------------- -//-------end of file: src\format.hpp -//-------------------------------------------------------- - - -namespace cru -{ - // A map with String as key and any type as value. - // It also has notification when value with specified key changed. - class AnyMap : public Object - { - public: - using ListenerToken = long; - using Listener = std::function<void(const std::any&)>; - - AnyMap() = default; - AnyMap(const AnyMap& other) = delete; - AnyMap(AnyMap&& other) = delete; - AnyMap& operator=(const AnyMap& other) = delete; - AnyMap& operator=(AnyMap&& other) = delete; - ~AnyMap() override = default; - - - // return the value if the value exists and the type of value is T. - // return a null optional if value doesn't exists. - // throw std::runtime_error if type is mismatch. - template <typename T> - std::optional<T> GetOptionalValue(const String& key) const - { - try - { - const auto find_result = map_.find(key); - if (find_result != map_.cend()) - { - const auto& value = find_result->second.first; - if (value.has_value()) - return std::any_cast<T>(value); - return std::nullopt; - } - return std::nullopt; - } - catch (const std::bad_any_cast&) - { - throw std::runtime_error(Format("Value of key \"{}\" in AnyMap is not of the type {}.", ToUtf8String(key), typeid(T).name())); - } - } - - // return the value if the value exists and the type of value is T. - // throw if value doesn't exists. (different from "GetOptionalValue"). - // throw std::runtime_error if type is mismatch. - template <typename T> - T GetValue(const String& key) const - { - const auto optional_value = GetOptionalValue<T>(key); - if (optional_value.has_value()) - return optional_value.value(); - else - throw std::runtime_error(Format("Key \"{}\" does not exists in AnyMap.", ToUtf8String(key))); - } - - // Set the value of key, and trigger all related listeners. - template <typename T> - void SetValue(const String& key, T&& value) - { - auto& p = map_[key]; - p.first = std::make_any<T>(std::forward<T>(value)); - InvokeListeners(p.second, p.first); - } - - // Remove the value of the key. - void ClearValue(const String& key) - { - auto& p = map_[key]; - p.first = std::any{}; - InvokeListeners(p.second, std::any{}); - } - - // Add a listener which is called when value of key is changed. - // Return a token used to remove the listener. - ListenerToken RegisterValueChangeListener(const String& key, const Listener& listener); - - // Remove a listener by token. - void UnregisterValueChangeListener(ListenerToken token); - - private: - void InvokeListeners(std::list<ListenerToken>& listener_list, const std::any& value); - - private: - std::unordered_map<String, std::pair<std::any, std::list<ListenerToken>>> map_{}; - std::unordered_map<ListenerToken, Listener> listeners_{}; - ListenerToken current_listener_token_ = 0; - }; -} -//-------------------------------------------------------- -//-------end of file: src\any_map.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\application.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -//-------------------------------------------------------- -//-------begin of file: src\system_headers.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -//include system headers - -#define NOMINMAX -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> -#include <windowsx.h> - -#pragma comment(lib, "D3D11.lib") -#include <d3d11.h> - -#pragma comment(lib, "D2d1.lib") -#include <d2d1_1.h> - -#pragma comment(lib, "DWrite.lib") -#include <dwrite.h> - -#include <dxgi1_2.h> -#include <wrl/client.h> - -#include <VersionHelpers.h> -//-------------------------------------------------------- -//-------end of file: src\system_headers.hpp -//-------------------------------------------------------- -#include <memory> -#include <optional> -#include <functional> -#include <typeindex> -#include <type_traits> - - -#ifdef CRU_DEBUG -#include <unordered_set> -#endif - -namespace cru -{ - class Application; - - namespace ui - { - class WindowClass; - } - - - class GodWindow : public Object - { - public: - explicit GodWindow(Application* application); - GodWindow(const GodWindow& other) = delete; - GodWindow(GodWindow&& other) = delete; - GodWindow& operator=(const GodWindow& other) = delete; - GodWindow& operator=(GodWindow&& other) = delete; - ~GodWindow() override; - - HWND GetHandle() const - { - return hwnd_; - } - - std::optional<LRESULT> HandleGodWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param); - - private: - std::unique_ptr<ui::WindowClass> god_window_class_; - HWND hwnd_; - }; - - class Application : public Object - { - public: - static Application* GetInstance(); - private: - static Application* instance_; - - public: - explicit Application(HINSTANCE h_instance); - Application(const Application&) = delete; - Application(Application&&) = delete; - Application& operator = (const Application&) = delete; - Application& operator = (Application&&) = delete; - ~Application() override; - - public: - int Run(); - void Quit(int quit_code); - - - HINSTANCE GetInstanceHandle() const - { - return h_instance_; - } - - GodWindow* GetGodWindow() const - { - return god_window_.get(); - } - - // Resolve a singleton. - // All singletons will be delete in reverse order of resolve. - template<typename T, typename = std::enable_if_t<std::is_base_of_v<Object, T>>> - T* ResolveSingleton(const std::function<T*(Application*)>& creator) - { - const auto& index = std::type_index{typeid(T)}; - const auto find_result = singleton_map_.find(index); - if (find_result != singleton_map_.cend()) - return static_cast<T*>(find_result->second); - -#ifdef CRU_DEBUG - const auto type_find_result = singleton_type_set_.find(index); - if (type_find_result != singleton_type_set_.cend()) - throw std::logic_error("The singleton of that type is being constructed. This may cause a dead recursion."); - singleton_type_set_.insert(index); -#endif - - auto singleton = creator(this); - singleton_map_.emplace(index, static_cast<Object*>(singleton)); - singleton_list_.push_back(singleton); - return singleton; - } - - private: - HINSTANCE h_instance_; - - std::unique_ptr<GodWindow> god_window_; - - std::unordered_map<std::type_index, Object*> singleton_map_; - std::list<Object*> singleton_list_; // used for reverse destroy. -#ifdef CRU_DEBUG - std::unordered_set<std::type_index> singleton_type_set_; // used for dead recursion. -#endif - }; - - - void InvokeLater(const std::function<void()>& action); -} -//-------------------------------------------------------- -//-------end of file: src\application.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\exception.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <optional> - - - -namespace cru { - class HResultError : public std::runtime_error - { - public: - explicit HResultError(HRESULT h_result, std::optional<MultiByteStringView> additional_message = std::nullopt); - HResultError(const HResultError& other) = default; - HResultError(HResultError&& other) = default; - HResultError& operator=(const HResultError& other) = default; - HResultError& operator=(HResultError&& other) = default; - ~HResultError() override = default; - - HRESULT GetHResult() const - { - return h_result_; - } - - private: - HRESULT h_result_; - }; - - inline void ThrowIfFailed(const HRESULT h_result) { - if (FAILED(h_result)) - throw HResultError(h_result); - } - - inline void ThrowIfFailed(const HRESULT h_result, const MultiByteStringView& message) { - if (FAILED(h_result)) - throw HResultError(h_result, message); - } - - class Win32Error : public std::runtime_error - { - public: - explicit Win32Error(DWORD error_code, std::optional<MultiByteStringView> additional_message = std::nullopt); - Win32Error(const Win32Error& other) = default; - Win32Error(Win32Error&& other) = default; - Win32Error& operator=(const Win32Error& other) = default; - Win32Error& operator=(Win32Error&& other) = default; - ~Win32Error() override = default; - - HRESULT GetErrorCode() const - { - return error_code_; - } - - private: - DWORD error_code_; - }; -} -//-------------------------------------------------------- -//-------end of file: src\exception.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\timer.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <map> -#include <chrono> -#include <functional> -#include <optional> - - -namespace cru -{ - using TimerAction = std::function<void()>; - - class TimerManager : public Object - { - public: - static TimerManager* GetInstance(); - - private: - TimerManager() = default; - public: - TimerManager(const TimerManager& other) = delete; - TimerManager(TimerManager&& other) = delete; - TimerManager& operator=(const TimerManager& other) = delete; - TimerManager& operator=(TimerManager&& other) = delete; - ~TimerManager() override = default; - - UINT_PTR CreateTimer(UINT milliseconds, bool loop, const TimerAction& action); - void KillTimer(UINT_PTR id); - std::optional<std::pair<bool, TimerAction>> GetAction(UINT_PTR id); - - private: - std::map<UINT_PTR, std::pair<bool, TimerAction>> map_{}; - UINT_PTR current_count_ = 0; - }; - - class TimerTask - { - friend TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action); - friend TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action); - - private: - explicit TimerTask(UINT_PTR id); - - public: - TimerTask(const TimerTask& other) = default; - TimerTask(TimerTask&& other) = default; - TimerTask& operator=(const TimerTask& other) = default; - TimerTask& operator=(TimerTask&& other) = default; - ~TimerTask() = default; - - void Cancel() const; - - private: - UINT_PTR id_; - }; - - TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action); - TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action); -} -//-------------------------------------------------------- -//-------end of file: src\timer.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\window.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <map> -#include <memory> - -//-------------------------------------------------------- -//-------begin of file: src\ui\control.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <unordered_map> -#include <any> -#include <utility> - -//-------------------------------------------------------- -//-------begin of file: src\ui\ui_base.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <optional> - - -namespace cru::ui -{ - struct Point - { - constexpr static Point Zero() - { - return Point(0, 0); - } - - constexpr Point() = default; - constexpr Point(const float x, const float y) : x(x), y(y) { } - - float x = 0; - float y = 0; - }; - - constexpr bool operator==(const Point& left, const Point& right) - { - return left.x == right.x && left.y == right.y; - } - - constexpr bool operator!=(const Point& left, const Point& right) - { - return !(left == right); - } - - struct Size - { - constexpr static Size Zero() - { - return Size(0, 0); - } - - constexpr Size() = default; - constexpr Size(const float width, const float height) : width(width), height(height) { } - - float width = 0; - float height = 0; - }; - - constexpr Size operator + (const Size& left, const Size& right) - { - return Size(left.width + right.width, left.height + right.height); - } - - constexpr Size operator - (const Size& left, const Size& right) - { - return Size(left.width - right.width, left.height - right.height); - } - - constexpr bool operator==(const Size& left, const Size& right) - { - return left.width == right.width && left.height == right.height; - } - - constexpr bool operator!=(const Size& left, const Size& right) - { - return !(left == right); - } - - struct Thickness - { - constexpr static Thickness Zero() - { - return Thickness(0); - } - - constexpr Thickness() : Thickness(0) { } - - constexpr explicit Thickness(const float width) - : left(width), top(width), right(width), bottom(width) { } - - constexpr explicit Thickness(const float horizontal, const float vertical) - : left(horizontal), top(vertical), right(horizontal), bottom(vertical) { } - - constexpr Thickness(const float left, const float top, const float right, const float bottom) - : left(left), top(top), right(right), bottom(bottom) { } - - float GetHorizontalTotal() const - { - return left + right; - } - - float GetVerticalTotal() const - { - return top + bottom; - } - - void SetLeftRight(const float value) - { - left = right = value; - } - - void SetTopBottom(const float value) - { - top = bottom = value; - } - - void SetAll(const float value) - { - left = top = right = bottom = value; - } - - float Validate() const - { - return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; - } - - float left; - float top; - float right; - float bottom; - }; - - struct Rect - { - constexpr Rect() = default; - constexpr Rect(const float left, const float top, const float width, const float height) - : left(left), top(top), width(width), height(height) { } - constexpr Rect(const Point& lefttop, const Size& size) - : left(lefttop.x), top(lefttop.y), width(size.width), height(size.height) { } - - constexpr static Rect FromVertices(const float left, const float top, const float right, const float bottom) - { - return Rect(left, top, right - left, bottom - top); - } - - constexpr float GetRight() const - { - return left + width; - } - - constexpr float GetBottom() const - { - return top + height; - } - - constexpr Point GetLeftTop() const - { - return Point(left, top); - } - - constexpr Point GetRightBottom() const - { - return Point(left + width, top + height); - } - - constexpr Point GetLeftBottom() const - { - return Point(left, top + height); - } - - constexpr Point GetRightTop() const - { - return Point(left + width, top); - } - - constexpr Size GetSize() const - { - return Size(width, height); - } - - constexpr Rect Shrink(const Thickness& thickness) const - { - return Rect(left + thickness.left, top + thickness.top, width - thickness.GetHorizontalTotal(), height - thickness.GetVerticalTotal()); - } - - constexpr bool IsPointInside(const Point& point) const - { - return - point.x >= left && - point.x < GetRight() && - point.y >= top && - point.y < GetBottom(); - } - - float left = 0.0f; - float top = 0.0f; - float width = 0.0f; - float height = 0.0f; - }; - - enum class MouseButton - { - Left, - Right, - Middle - }; - - struct TextRange - { - constexpr static std::optional<TextRange> FromTwoSides(unsigned first, unsigned second) - { - if (first > second) - return std::make_optional<TextRange>(second, first - second); - if (first < second) - return std::make_optional<TextRange>(first, second - first); - return std::nullopt; - } - - constexpr static std::pair<unsigned, unsigned> ToTwoSides(std::optional<TextRange> text_range, unsigned default_position = 0) - { - if (text_range.has_value()) - return std::make_pair(text_range.value().position, text_range.value().position + text_range.value().count); - return std::make_pair(default_position, default_position); - } - - constexpr TextRange() = default; - constexpr TextRange(const unsigned position, const unsigned count) - : position(position), count(count) - { - - } - - unsigned position = 0; - unsigned count = 0; - }; - - bool IsKeyDown(int virtual_code); - bool IsKeyToggled(int virtual_code); - bool IsAnyMouseButtonDown(); -} -//-------------------------------------------------------- -//-------end of file: src\ui\ui_base.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\layout_base.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - -namespace cru::ui -{ - enum class Alignment - { - Center, - Start, - End - }; - - enum class MeasureMode - { - Exactly, - Content, - Stretch - }; - - enum class RectRange - { - Content, // content excluding padding, border and margin - Padding, // only including content and padding - HalfBorder, // including content, padding and half border - FullBorder, // including content, padding and full border - Margin // including content, padding, border and margin - }; - - struct LayoutSideParams final - { - constexpr static LayoutSideParams Exactly(const float length, const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Exactly, length, alignment); - } - - constexpr static LayoutSideParams Content(const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Content, 0, alignment); - } - - constexpr static LayoutSideParams Stretch(const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Stretch, 0, alignment); - } - - constexpr LayoutSideParams() = default; - - constexpr explicit LayoutSideParams(const MeasureMode mode, const float length, const Alignment alignment) - : length(length), mode(mode), alignment(alignment) - { - - } - - constexpr bool Validate() const - { - if (length < 0.0) - return false; - if (min.has_value() && min.value() < 0.0) - return false; - if (max.has_value() && max.value() < 0.0) - return false; - if (min.has_value() && max.has_value() && min.value() > max.value()) - return false; - return true; - } - - // only used in exactly mode, specify the exactly side length of content. - float length = 0.0; - MeasureMode mode = MeasureMode::Content; - Alignment alignment = Alignment::Center; - - // min and max specify the min/max side length of content. - // they are used as hint and respect the actual size that content needs. - // when mode is exactly, length is coerced into the min-max range. - std::optional<float> min = std::nullopt; - std::optional<float> max = std::nullopt; - }; - - struct BasicLayoutParams final - { - BasicLayoutParams() = default; - BasicLayoutParams(const BasicLayoutParams&) = default; - BasicLayoutParams(BasicLayoutParams&&) = default; - BasicLayoutParams& operator = (const BasicLayoutParams&) = default; - BasicLayoutParams& operator = (BasicLayoutParams&&) = default; - ~BasicLayoutParams() = default; - - bool Validate() const - { - return width.Validate() && height.Validate() && margin.Validate() && padding.Validate(); - } - - LayoutSideParams width; - LayoutSideParams height; - Thickness padding; - Thickness margin; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\layout_base.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\events\ui_event.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <optional> - -//-------------------------------------------------------- -//-------begin of file: src\cru_event.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <type_traits> -#include <functional> -#include <map> - - -namespace cru { - //Base class of all event args. - class BasicEventArgs : public Object - { - public: - explicit BasicEventArgs(Object* sender) - : sender_(sender) - { - - } - BasicEventArgs(const BasicEventArgs& other) = default; - BasicEventArgs(BasicEventArgs&& other) = default; - BasicEventArgs& operator=(const BasicEventArgs& other) = default; - BasicEventArgs& operator=(BasicEventArgs&& other) = default; - ~BasicEventArgs() override = default; - - //Get the sender of the event. - Object* GetSender() const - { - return sender_; - } - - private: - Object* sender_; - }; - - - //A non-copyable non-movable Event class. - //It stores a list of event handlers. - //TArgsType must be subclass of BasicEventArgs. - template<typename TArgsType> - class Event - { - public: - static_assert(std::is_base_of_v<BasicEventArgs, TArgsType>, - "TArgsType must be subclass of BasicEventArgs."); - - - using ArgsType = TArgsType; - using EventHandler = std::function<void(ArgsType&)>; - using EventHandlerToken = long; - - Event() = default; - Event(const Event&) = delete; - Event& operator = (const Event&) = delete; - Event(Event&&) = delete; - Event& operator = (Event&&) = delete; - ~Event() = default; - - EventHandlerToken AddHandler(const EventHandler& handler) - { - const auto token = current_token_++; - handlers_.emplace(token, handler); - return token; - } - - void RemoveHandler(const EventHandlerToken token) { - auto find_result = handlers_.find(token); - if (find_result != handlers_.cend()) - handlers_.erase(find_result); - } - - void Raise(ArgsType& args) { - for (const auto& handler : handlers_) - (handler.second)(args); - } - - private: - std::map<EventHandlerToken, EventHandler> handlers_; - - EventHandlerToken current_token_ = 0; - }; -} -//-------------------------------------------------------- -//-------end of file: src\cru_event.hpp -//-------------------------------------------------------- - -namespace cru::ui -{ - class Control; -} - -namespace cru::ui::events -{ - class UiEventArgs : public BasicEventArgs - { - public: - UiEventArgs(Object* sender, Object* original_sender) - : BasicEventArgs(sender), original_sender_(original_sender), handled_(false) - { - - } - - UiEventArgs(const UiEventArgs& other) = default; - UiEventArgs(UiEventArgs&& other) = default; - UiEventArgs& operator=(const UiEventArgs& other) = default; - UiEventArgs& operator=(UiEventArgs&& other) = default; - ~UiEventArgs() override = default; - - Object* GetOriginalSender() const - { - return original_sender_; - } - - bool IsHandled() const - { - return handled_; - } - - void SetHandled(const bool handled = true) - { - handled_ = handled; - } - - private: - Object* original_sender_; - bool handled_; - }; - - template <typename TEventArgs> - class RoutedEvent - { - public: - static_assert(std::is_base_of_v<UiEventArgs, TEventArgs>, "TEventArgs must be subclass of UiEventArgs."); - - using EventArgs = TEventArgs; - - RoutedEvent() = default; - RoutedEvent(const RoutedEvent& other) = delete; - RoutedEvent(RoutedEvent&& other) = delete; - RoutedEvent& operator=(const RoutedEvent& other) = delete; - RoutedEvent& operator=(RoutedEvent&& other) = delete; - ~RoutedEvent() = default; - - Event<TEventArgs> direct; - Event<TEventArgs> bubble; - Event<TEventArgs> tunnel; - }; - - class MouseEventArgs : public UiEventArgs - { - public: - MouseEventArgs(Object* sender, Object* original_sender, const std::optional<Point>& point = std::nullopt) - : UiEventArgs(sender, original_sender), point_(point) - { - - } - MouseEventArgs(const MouseEventArgs& other) = default; - MouseEventArgs(MouseEventArgs&& other) = default; - MouseEventArgs& operator=(const MouseEventArgs& other) = default; - MouseEventArgs& operator=(MouseEventArgs&& other) = default; - ~MouseEventArgs() override = default; - - Point GetPoint(Control* control, RectRange range = RectRange::Content) const; - - private: - std::optional<Point> point_; - }; - - - class MouseButtonEventArgs : public MouseEventArgs - { - public: - MouseButtonEventArgs(Object* sender, Object* original_sender, const Point& point, const MouseButton button) - : MouseEventArgs(sender, original_sender, point), button_(button) - { - - } - MouseButtonEventArgs(const MouseButtonEventArgs& other) = default; - MouseButtonEventArgs(MouseButtonEventArgs&& other) = default; - MouseButtonEventArgs& operator=(const MouseButtonEventArgs& other) = default; - MouseButtonEventArgs& operator=(MouseButtonEventArgs&& other) = default; - ~MouseButtonEventArgs() override = default; - - MouseButton GetMouseButton() const - { - return button_; - } - - private: - MouseButton button_; - }; - - - class MouseWheelEventArgs : public MouseEventArgs - { - public: - MouseWheelEventArgs(Object* sender, Object* original_sender, const Point& point, const float delta) - : MouseEventArgs(sender, original_sender, point), delta_(delta) - { - - } - MouseWheelEventArgs(const MouseWheelEventArgs& other) = default; - MouseWheelEventArgs(MouseWheelEventArgs&& other) = default; - MouseWheelEventArgs& operator=(const MouseWheelEventArgs& other) = default; - MouseWheelEventArgs& operator=(MouseWheelEventArgs&& other) = default; - ~MouseWheelEventArgs() override = default; - - float GetDelta() const - { - return delta_; - } - - private: - float delta_; - }; - - - class DrawEventArgs : public UiEventArgs - { - public: - DrawEventArgs(Object* sender, Object* original_sender, ID2D1DeviceContext* device_context) - : UiEventArgs(sender, original_sender), device_context_(device_context) - { - - } - DrawEventArgs(const DrawEventArgs& other) = default; - DrawEventArgs(DrawEventArgs&& other) = default; - DrawEventArgs& operator=(const DrawEventArgs& other) = default; - DrawEventArgs& operator=(DrawEventArgs&& other) = default; - ~DrawEventArgs() = default; - - ID2D1DeviceContext* GetDeviceContext() const - { - return device_context_; - } - - private: - ID2D1DeviceContext * device_context_; - }; - - - class FocusChangeEventArgs : public UiEventArgs - { - public: - FocusChangeEventArgs(Object* sender, Object* original_sender, const bool is_window = false) - : UiEventArgs(sender, original_sender), is_window_(is_window) - { - - } - FocusChangeEventArgs(const FocusChangeEventArgs& other) = default; - FocusChangeEventArgs(FocusChangeEventArgs&& other) = default; - FocusChangeEventArgs& operator=(const FocusChangeEventArgs& other) = default; - FocusChangeEventArgs& operator=(FocusChangeEventArgs&& other) = default; - ~FocusChangeEventArgs() override = default; - - // Return whether the focus change is caused by the window-wide focus change. - bool IsWindow() const - { - return is_window_; - } - - private: - bool is_window_; - }; - - class ToggleEventArgs : public UiEventArgs - { - public: - ToggleEventArgs(Object* sender, Object* original_sender, bool new_state) - : UiEventArgs(sender, original_sender), new_state_(new_state) - { - - } - ToggleEventArgs(const ToggleEventArgs& other) = default; - ToggleEventArgs(ToggleEventArgs&& other) = default; - ToggleEventArgs& operator=(const ToggleEventArgs& other) = default; - ToggleEventArgs& operator=(ToggleEventArgs&& other) = default; - ~ToggleEventArgs() override = default; - - bool GetNewState() const - { - return new_state_; - } - - private: - bool new_state_; - }; - - struct WindowNativeMessage - { - HWND hwnd; - int msg; - WPARAM w_param; - LPARAM l_param; - }; - - class WindowNativeMessageEventArgs : public UiEventArgs - { - public: - WindowNativeMessageEventArgs(Object* sender, Object* original_sender, const WindowNativeMessage& message) - : UiEventArgs(sender, original_sender), message_(message), result_(std::nullopt) - { - - } - WindowNativeMessageEventArgs(const WindowNativeMessageEventArgs& other) = default; - WindowNativeMessageEventArgs(WindowNativeMessageEventArgs&& other) = default; - WindowNativeMessageEventArgs& operator=(const WindowNativeMessageEventArgs& other) = default; - WindowNativeMessageEventArgs& operator=(WindowNativeMessageEventArgs&& other) = default; - ~WindowNativeMessageEventArgs() override = default; - - WindowNativeMessage GetWindowMessage() const - { - return message_; - } - - std::optional<LRESULT> GetResult() const - { - return result_; - } - - void SetResult(const std::optional<LRESULT> result) - { - result_ = result; - } - - private: - WindowNativeMessage message_; - std::optional<LRESULT> result_; - }; - - class KeyEventArgs : public UiEventArgs - { - public: - KeyEventArgs(Object* sender, Object* original_sender, int virtual_code) - : UiEventArgs(sender, original_sender), virtual_code_(virtual_code) - { - } - KeyEventArgs(const KeyEventArgs& other) = default; - KeyEventArgs(KeyEventArgs&& other) = default; - KeyEventArgs& operator=(const KeyEventArgs& other) = default; - KeyEventArgs& operator=(KeyEventArgs&& other) = default; - ~KeyEventArgs() override = default; - - int GetVirtualCode() const - { - return virtual_code_; - } - - private: - int virtual_code_; - }; - - class CharEventArgs : public UiEventArgs - { - public: - CharEventArgs(Object* sender, Object* original_sender, wchar_t c) - : UiEventArgs(sender, original_sender), c_(c) - { - } - CharEventArgs(const CharEventArgs& other) = default; - CharEventArgs(CharEventArgs&& other) = default; - CharEventArgs& operator=(const CharEventArgs& other) = default; - CharEventArgs& operator=(CharEventArgs&& other) = default; - ~CharEventArgs() override = default; - - wchar_t GetChar() const - { - return c_; - } - - private: - wchar_t c_; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\events\ui_event.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\border_property.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - - - -namespace cru::ui -{ - class BorderProperty final - { - public: - BorderProperty(); - explicit BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush); - BorderProperty(Microsoft::WRL::ComPtr<ID2D1Brush> brush, float width, float radius_x, float radius_y, Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style = nullptr); - BorderProperty(const BorderProperty& other) = default; - BorderProperty(BorderProperty&& other) = default; - BorderProperty& operator=(const BorderProperty& other) = default; - BorderProperty& operator=(BorderProperty&& other) = default; - ~BorderProperty() = default; - - - Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const - { - return brush_; - } - - float GetStrokeWidth() const - { - return stroke_width_; - } - - Microsoft::WRL::ComPtr<ID2D1StrokeStyle> GetStrokeStyle() const - { - return stroke_style_; - } - - float GetRadiusX() const - { - return radius_x_; - } - - float GetRadiusY() const - { - return radius_y_; - } - - void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> brush) - { - Require(brush == nullptr, "Brush of BorderProperty mustn't be null."); - brush_ = std::move(brush); - } - - void SetStrokeWidth(const float stroke_width) - { - Require(stroke_width >= 0.0f, "Stroke width must be no less than 0."); - stroke_width_ = stroke_width; - } - - void SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style) - { - stroke_style_ = std::move(stroke_style); - } - - void SetRadiusX(const float radius_x) - { - Require(radius_x >= 0.0f, "Radius-x must be no less than 0."); - radius_x_ = radius_x; - } - - void SetRadiusY(const float radius_y) - { - Require(radius_y >= 0.0f, "Radius-y must be no less than 0."); - radius_y_ = radius_y; - } - - private: - Microsoft::WRL::ComPtr<ID2D1Brush> brush_; - float stroke_width_ = 1.0f; - float radius_x_ = 0.0f; - float radius_y_ = 0.0f; - Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style_ = nullptr; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\border_property.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\cursor.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <memory> - - -namespace cru::ui -{ - class Cursor : public Object - { - public: - using Ptr = std::shared_ptr<Cursor>; - - Cursor(HCURSOR handle, bool auto_release); - Cursor(const Cursor& other) = delete; - Cursor(Cursor&& other) = delete; - Cursor& operator=(const Cursor& other) = delete; - Cursor& operator=(Cursor&& other) = delete; - ~Cursor() override; - - HCURSOR GetHandle() const - { - return handle_; - } - - private: - HCURSOR handle_; - bool auto_release_; - }; - - namespace cursors - { - extern Cursor::Ptr arrow; - extern Cursor::Ptr hand; - extern Cursor::Ptr i_beam; - - void LoadSystemCursors(); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\cursor.hpp -//-------------------------------------------------------- - -namespace cru::ui -{ - class Window; - - - struct AdditionalMeasureInfo - { - bool horizontal_stretchable = true; - bool vertical_stretchable = true; - }; - - struct AdditionalLayoutInfo - { - Point total_offset = Point::Zero(); - }; - - //the position cache - struct ControlPositionCache - { - //The lefttop relative to the ancestor. - Point lefttop_position_absolute = Point::Zero(); - }; - - - class Control : public Object - { - friend class Window; - - protected: - struct GeometryInfo - { - Microsoft::WRL::ComPtr<ID2D1Geometry> border_geometry = nullptr; - Microsoft::WRL::ComPtr<ID2D1Geometry> padding_content_geometry = nullptr; - Microsoft::WRL::ComPtr<ID2D1Geometry> content_geometry = nullptr; - }; - - - protected: - Control(); - public: - Control(const Control& other) = delete; - Control(Control&& other) = delete; - Control& operator=(const Control& other) = delete; - Control& operator=(Control&& other) = delete; - ~Control() override = default; - - public: - - //*************** region: tree *************** - virtual StringView GetControlType() const = 0; - - virtual const std::vector<Control*>& GetInternalChildren() const = 0; - - Control* GetParent() const - { - return parent_ == nullptr ? internal_parent_ : parent_; - } - - Control* GetInternalParent() const - { - return internal_parent_; - } - - //Get the window if attached, otherwise, return nullptr. - Window* GetWindow() const - { - return window_; - } - - void SetParent(Control* parent); - - void SetInternalParent(Control* internal_parent); - - void SetDescendantWindow(Window* window); - - - //Traverse the tree rooted the control including itself. - void TraverseDescendants(const std::function<void(Control*)>& predicate); - - //*************** region: position and size *************** - - //Get the lefttop relative to its parent. - virtual Point GetOffset(); - - //Get the actual size. - virtual Size GetSize(); - - // If offset changes, call RefreshDescendantPositionCache. - virtual void SetRect(const Rect& rect); - - //Get lefttop relative to ancestor. This is only valid when - //attached to window. Notice that the value is cached. - //You can invalidate and recalculate it by calling "InvalidatePositionCache". - Point GetPositionAbsolute() const; - - //Local point to absolute point. - Point ControlToWindow(const Point& point) const; - - //Absolute point to local point. - Point WindowToControl(const Point& point) const; - - void RefreshDescendantPositionCache(); - - private: - static void RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute); - - public: - - // Default implement in Control is test point in border geometry's - // fill and stroke with width of border. - virtual bool IsPointInside(const Point& point); - - // Get the top control among all descendants (including self) in local coordinate. - virtual Control* HitTest(const Point& point); - - //*************** region: graphic *************** - - bool IsClipContent() const - { - return clip_content_; - } - - void SetClipContent(bool clip); - - //Draw this control and its child controls. - void Draw(ID2D1DeviceContext* device_context); - - virtual void InvalidateDraw(); - - Microsoft::WRL::ComPtr<ID2D1Brush> GetForegroundBrush() const - { - return foreground_brush_; - } - - void SetForegroundBrush(Microsoft::WRL::ComPtr<ID2D1Brush> foreground_brush) - { - foreground_brush_ = std::move(foreground_brush); - InvalidateDraw(); - } - - Microsoft::WRL::ComPtr<ID2D1Brush> GetBackgroundBrush() const - { - return background_brush_; - } - - void SetBackgroundBrush(Microsoft::WRL::ComPtr<ID2D1Brush> background_brush) - { - background_brush_ = std::move(background_brush); - InvalidateDraw(); - } - - - //*************** region: focus *************** - - bool RequestFocus(); - - bool HasFocus(); - - bool IsFocusOnPressed() const - { - return is_focus_on_pressed_; - } - - void SetFocusOnPressed(const bool value) - { - is_focus_on_pressed_ = value; - } - - //*************** region: layout *************** - - void InvalidateLayout(); - - void Measure(const Size& available_size, const AdditionalMeasureInfo& additional_info); - - void Layout(const Rect& rect, const AdditionalLayoutInfo& additional_info); - - Size GetDesiredSize() const; - - void SetDesiredSize(const Size& desired_size); - - BasicLayoutParams* GetLayoutParams() - { - return &layout_params_; - } - - Rect GetRect(RectRange range); - - Point TransformPoint(const Point& point, RectRange from = RectRange::Margin, RectRange to = RectRange::Content); - - //*************** region: border *************** - - BorderProperty& GetBorderProperty() - { - return border_property_; - } - - void UpdateBorder(); - - bool IsBordered() const - { - return is_bordered_; - } - - void SetBordered(bool bordered); - - - //*************** region: additional properties *************** - AnyMap* GetAdditionalPropertyMap() - { - return &additional_property_map_; - } - - - //*************** region: cursor *************** - // If cursor is set to null, then it uses parent's cursor. - // Window's cursor can't be null. - - Cursor::Ptr GetCursor() const - { - return cursor_; - } - - void SetCursor(const Cursor::Ptr& cursor); - - - //*************** region: events *************** - //Raised when mouse enter the control. - events::RoutedEvent<events::MouseEventArgs> mouse_enter_event; - //Raised when mouse is leave the control. - events::RoutedEvent<events::MouseEventArgs> mouse_leave_event; - //Raised when mouse is move in the control. - events::RoutedEvent<events::MouseEventArgs> mouse_move_event; - //Raised when a mouse button is pressed in the control. - events::RoutedEvent<events::MouseButtonEventArgs> mouse_down_event; - //Raised when a mouse button is released in the control. - events::RoutedEvent<events::MouseButtonEventArgs> mouse_up_event; - //Raised when a mouse button is pressed in the control and released in the control with mouse not leaving it between two operations. - events::RoutedEvent<events::MouseButtonEventArgs> mouse_click_event; - - events::RoutedEvent<events::MouseWheelEventArgs> mouse_wheel_event; - - events::RoutedEvent<events::KeyEventArgs> key_down_event; - events::RoutedEvent<events::KeyEventArgs> key_up_event; - events::RoutedEvent<events::CharEventArgs> char_event; - - events::RoutedEvent<events::FocusChangeEventArgs> get_focus_event; - events::RoutedEvent<events::FocusChangeEventArgs> lose_focus_event; - - Event<events::DrawEventArgs> draw_content_event; - Event<events::DrawEventArgs> draw_background_event; - Event<events::DrawEventArgs> draw_foreground_event; - - - //*************** region: tree event *************** - protected: - virtual void OnParentChanged(Control* old_parent, Control* new_parent); - - virtual void OnInternalParentChanged(Control* old_internal_parent, Control* new_internal_parent); - - //Invoked when the control is attached to a window. Overrides should invoke base. - virtual void OnAttachToWindow(Window* window); - //Invoked when the control is detached to a window. Overrides should invoke base. - virtual void OnDetachToWindow(Window* window); - - - //*************** region: graphic event *************** - private: - void OnDrawDecoration(ID2D1DeviceContext* device_context); - void OnDrawCore(ID2D1DeviceContext* device_context); - - - //*************** region: position and size event *************** - protected: - virtual void OnRectChange(const Rect& old_rect, const Rect& new_rect); - - void RegenerateGeometryInfo(); - - const GeometryInfo& GetGeometryInfo() const - { - return geometry_info_; - } - - - //*************** region: mouse event *************** - protected: - virtual void OnMouseClickBegin(MouseButton button); - virtual void OnMouseClickEnd(MouseButton button); - - - //*************** region: layout *************** - private: - Size OnMeasureCore(const Size& available_size, const AdditionalMeasureInfo& additional_info); - void OnLayoutCore(const Rect& rect, const AdditionalLayoutInfo& additional_info); - - protected: - virtual Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) = 0; - virtual void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) = 0; - - private: - Window * window_ = nullptr; - Control* parent_ = nullptr; // when parent and internal parent are the same, parent_ is nullptr. - Control * internal_parent_ = nullptr; - - Rect rect_{}; - - ControlPositionCache position_cache_{}; - - std::unordered_map<MouseButton, bool> is_mouse_click_valid_map_ - { - { MouseButton::Left, true }, - { MouseButton::Middle, true }, - { MouseButton::Right, true } - }; // used for clicking determination - - BasicLayoutParams layout_params_{}; - Size desired_size_ = Size::Zero(); - - bool is_bordered_ = false; - BorderProperty border_property_; - - GeometryInfo geometry_info_{}; - - bool clip_content_ = false; - - Microsoft::WRL::ComPtr<ID2D1Brush> foreground_brush_ = nullptr; - Microsoft::WRL::ComPtr<ID2D1Brush> background_brush_ = nullptr; - - AnyMap additional_property_map_{}; - - bool is_focus_on_pressed_ = true; - -#ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr<ID2D1Geometry> margin_geometry_; - Microsoft::WRL::ComPtr<ID2D1Geometry> padding_geometry_; -#endif - - Cursor::Ptr cursor_{}; - }; - - - - class NoChildControl : public Control - { - private: - // used in GetInternalChildren. - static const std::vector<Control*> empty_control_vector; - - protected: - NoChildControl() = default; - public: - NoChildControl(const NoChildControl& other) = delete; - NoChildControl(NoChildControl&& other) = delete; - NoChildControl& operator=(const NoChildControl& other) = delete; - NoChildControl& operator=(NoChildControl&& other) = delete; - ~NoChildControl() override = default; - - const std::vector<Control*>& GetInternalChildren() const override final - { - return empty_control_vector; - } - - protected: - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override; - }; - - - class SingleChildControl : public Control - { - protected: - SingleChildControl(); - public: - SingleChildControl(const SingleChildControl& other) = delete; - SingleChildControl(SingleChildControl&& other) = delete; - SingleChildControl& operator=(const SingleChildControl& other) = delete; - SingleChildControl& operator=(SingleChildControl&& other) = delete; - ~SingleChildControl() override; - - const std::vector<Control*>& GetInternalChildren() const override final - { - return child_vector_; - } - - Control* GetChild() const - { - return child_; - } - - void SetChild(Control* child); - - protected: - // Override should call base. - virtual void OnChildChanged(Control* old_child, Control* new_child); - - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override; - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override; - - private: - std::vector<Control*> child_vector_; - Control*& child_; - }; - - - class MultiChildControl : public Control - { - protected: - MultiChildControl() = default; - public: - MultiChildControl(const MultiChildControl& other) = delete; - MultiChildControl(MultiChildControl&& other) = delete; - MultiChildControl& operator=(const MultiChildControl& other) = delete; - MultiChildControl& operator=(MultiChildControl&& other) = delete; - ~MultiChildControl() override; - - const std::vector<Control*>& GetInternalChildren() const override final - { - return children_; - } - - const std::vector<Control*>& GetChildren() const - { - return children_; - } - - //Add a child at tail. - void AddChild(Control* control); - - //Add a child before the position. - void AddChild(Control* control, int position); - - //Remove a child. - void RemoveChild(Control* child); - - //Remove a child at specified position. - void RemoveChild(int position); - - protected: - //Invoked when a child is added. Overrides should invoke base. - virtual void OnAddChild(Control* child); - //Invoked when a child is removed. Overrides should invoke base. - virtual void OnRemoveChild(Control* child); - - private: - std::vector<Control*> children_; - }; - - - - //*************** region: event dispatcher helper *************** - - // Dispatch the event. - // - // This will raise routed event of the control and its parent and parent's - // parent ... (until "last_receiver" if it's not nullptr) with appropriate args. - // - // First tunnel from top to bottom possibly stopped by "handled" flag in EventArgs. - // Second bubble from bottom to top possibly stopped by "handled" flag in EventArgs. - // Last direct to each control. - // - // Args is of type "EventArgs". The first init argument is "sender", which is - // automatically bound to each receiving control. The second init argument is - // "original_sender", which is unchanged. And "args" will be perfectly forwarded - // as the rest arguments. - template<typename EventArgs, typename... Args> - void DispatchEvent(Control* const original_sender, events::RoutedEvent<EventArgs> Control::* event_ptr, Control* const last_receiver, Args&&... args) - { - std::list<Control*> receive_list; - - auto parent = original_sender; - while (parent != last_receiver) - { - receive_list.push_back(parent); - parent = parent->GetInternalParent(); - } - - auto handled = false; - - //tunnel - for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) - { - EventArgs event_args(*i, original_sender, std::forward<Args>(args)...); - (*i->*event_ptr).tunnel.Raise(event_args); - if (event_args.IsHandled()) - { - handled = true; - break; - } - } - - //bubble - if (!handled) - { - for (auto i : receive_list) - { - EventArgs event_args(i, original_sender, std::forward<Args>(args)...); - (i->*event_ptr).bubble.Raise(event_args); - if (event_args.IsHandled()) - break; - } - } - - //direct - for (auto i : receive_list) - { - EventArgs event_args(i, original_sender, std::forward<Args>(args)...); - (i->*event_ptr).direct.Raise(event_args); - } - } - - - //*************** region: tree helper *************** - - // Find the lowest common ancestor. - // Return nullptr if "left" and "right" are not in the same tree. - Control* FindLowestCommonAncestor(Control* left, Control* right); - - - //*************** region: create helper *************** - - template <typename TControl, typename... Args> - TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, Args&&... args) - { - static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward<Args>(args)...); - control->GetLayoutParams()->width = width; - control->GetLayoutParams()->height = height; - return control; - } - - template <typename TControl, typename... Args> - TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args) - { - static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward<Args>(args)...); - control->GetLayoutParams()->padding = padding; - control->GetLayoutParams()->margin = margin; - return control; - } - - template <typename TControl, typename... Args> - TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, const Thickness& padding, const Thickness& margin, Args&&... args) - { - static_assert(std::is_base_of_v<Control, TControl>, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward<Args>(args)...); - control->GetLayoutParams()->width = width; - control->GetLayoutParams()->height = height; - control->GetLayoutParams()->padding = padding; - control->GetLayoutParams()->margin = margin; - return control; - } - - using ControlList = std::initializer_list<Control*>; -} -//-------------------------------------------------------- -//-------end of file: src\ui\control.hpp -//-------------------------------------------------------- - -namespace cru::graph -{ - class WindowRenderTarget; -} - -namespace cru::ui -{ - class WindowClass : public Object - { - public: - WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance); - WindowClass(const WindowClass& other) = delete; - WindowClass(WindowClass&& other) = delete; - WindowClass& operator=(const WindowClass& other) = delete; - WindowClass& operator=(WindowClass&& other) = delete; - ~WindowClass() override = default; - - - const wchar_t* GetName() const - { - return name_.c_str(); - } - - ATOM GetAtom() const - { - return atom_; - } - - private: - String name_; - ATOM atom_; - }; - - class WindowManager : public Object - { - public: - static WindowManager* GetInstance(); - private: - WindowManager(); - public: - WindowManager(const WindowManager& other) = delete; - WindowManager(WindowManager&& other) = delete; - WindowManager& operator=(const WindowManager& other) = delete; - WindowManager& operator=(WindowManager&& other) = delete; - ~WindowManager() override = default; - - - //Get the general window class for creating ordinary window. - WindowClass* GetGeneralWindowClass() const - { - return general_window_class_.get(); - } - - //Register a window newly created. - //This function adds the hwnd to hwnd-window map. - //It should be called immediately after a window was created. - void RegisterWindow(HWND hwnd, Window* window); - - //Unregister a window that is going to be destroyed. - //This function removes the hwnd from the hwnd-window map. - //It should be called immediately before a window is going to be destroyed, - void UnregisterWindow(HWND hwnd); - - //Return a pointer to the Window object related to the HWND or nullptr if the hwnd is not in the map. - Window* FromHandle(HWND hwnd); - - std::vector<Window*> GetAllWindows() const; - - private: - std::unique_ptr<WindowClass> general_window_class_; - std::map<HWND, Window*> window_map_; - }; - - - - class Window final : public SingleChildControl - { - friend class WindowManager; - public: - static constexpr auto control_type = L"Window"; - - public: - static Window* CreateOverlapped(); - static Window* CreatePopup(Window* parent, bool caption = false); - - private: - struct tag_overlapped_constructor {}; - struct tag_popup_constructor {}; - - explicit Window(tag_overlapped_constructor); - Window(tag_popup_constructor, Window* parent, bool caption); - - void BeforeCreateHwnd(); - void AfterCreateHwnd(WindowManager* window_manager); - - public: - Window(const Window& other) = delete; - Window(Window&& other) = delete; - Window& operator=(const Window& other) = delete; - Window& operator=(Window&& other) = delete; - ~Window() override; - - public: - StringView GetControlType() const override final; - - void SetDeleteThisOnDestroy(bool value); - - //*************** region: handle *************** - - //Get the handle of the window. Return null if window is invalid. - HWND GetWindowHandle() const - { - return hwnd_; - } - - //Return if the window is still valid, that is, hasn't been closed or destroyed. - bool IsWindowValid() const - { - return hwnd_ != nullptr; - } - - - //*************** region: window operations *************** - - Window* GetParentWindow() const - { - return parent_window_; - } - - //Close and destroy the window if the window is valid. - void Close(); - - //Send a repaint message to the window's message queue which may make the window repaint. - void InvalidateDraw() override final; - - //Show the window. - void Show(); - - //Hide thw window. - void Hide(); - - //Get the client size. - Size GetClientSize(); - - //Set the client size and repaint. - void SetClientSize(const Size& size); - - //Get the rect of the window containing frame. - //The lefttop of the rect is relative to screen lefttop. - Rect GetWindowRect(); - - //Set the rect of the window containing frame. - //The lefttop of the rect is relative to screen lefttop. - void SetWindowRect(const Rect& rect); - - //Set the lefttop of the window relative to screen. - void SetWindowPosition(const Point& position); - - Point PointToScreen(const Point& point); - - Point PointFromScreen(const Point& point); - - //Handle the raw window message. - //Return true if the message is handled and get the result through "result" argument. - //Return false if the message is not handled. - bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT& result); - - //*************** region: mouse *************** - - Point GetMousePosition(); - - Control* GetMouseHoverControl() const - { - return mouse_hover_control_; - } - - //*************** region: position and size *************** - - //Always return (0, 0) for a window. - Point GetOffset() override final; - - //Get the size of client area for a window. - Size GetSize() override final; - - //This method has no effect for a window. Use SetClientSize instead. - void SetRect(const Rect& size) override final; - - //Override. If point is in client area, it is in window. - bool IsPointInside(const Point& point) override final; - - //*************** region: layout *************** - - void WindowInvalidateLayout(); - - void Relayout(); - - void SetSizeFitContent(const Size& max_size = Size(1000, 1000)); - - - //*************** region: focus *************** - - //Request focus for specified control. - bool RequestFocusFor(Control* control); - - //Get the control that has focus. - Control* GetFocusControl(); - - - //*************** region: mouse capture *************** - - Control* CaptureMouseFor(Control* control); - Control* ReleaseCurrentMouseCapture(); - - - //*************** region: cursor *************** - void UpdateCursor(); - - //*************** region: debug *************** -#ifdef CRU_DEBUG_LAYOUT - bool IsDebugLayout() const - { - return debug_layout_; - } - - void SetDebugLayout(bool value); -#endif - - public: - //*************** region: events *************** - Event<events::UiEventArgs> activated_event; - Event<events::UiEventArgs> deactivated_event; - - Event<events::WindowNativeMessageEventArgs> native_message_event; - - private: - //*************** region: native operations *************** - - //Get the client rect in pixel. - RECT GetClientRectPixel(); - - bool IsMessageInQueue(UINT message); - - void SetCursorInternal(HCURSOR cursor); - - - //*************** region: native messages *************** - - void OnDestroyInternal(); - void OnPaintInternal(); - void OnResizeInternal(int new_width, int new_height); - - void OnSetFocusInternal(); - void OnKillFocusInternal(); - - void OnMouseMoveInternal(POINT point); - void OnMouseLeaveInternal(); - void OnMouseDownInternal(MouseButton button, POINT point); - void OnMouseUpInternal(MouseButton button, POINT point); - - void OnMouseWheelInternal(short delta, POINT point); - void OnKeyDownInternal(int virtual_code); - void OnKeyUpInternal(int virtual_code); - void OnCharInternal(wchar_t c); - - void OnActivatedInternal(); - void OnDeactivatedInternal(); - - //*************** region: event dispatcher helper *************** - - void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point); - - private: - bool delete_this_on_destroy_ = true; - - HWND hwnd_ = nullptr; - Window* parent_window_ = nullptr; - std::shared_ptr<graph::WindowRenderTarget> render_target_{}; - - Control* mouse_hover_control_ = nullptr; - - bool window_focus_ = false; - Control* focus_control_ = this; // "focus_control_" can't be nullptr - - Control* mouse_capture_control_ = nullptr; - - bool is_layout_invalid_ = false; - -#ifdef CRU_DEBUG_LAYOUT - bool debug_layout_ = false; -#endif - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\window.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\cru_debug.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <functional> - - -namespace cru::debug -{ - void DebugMessage(const StringView& message); - -#ifdef CRU_DEBUG - inline void DebugTime(const std::function<void()>& action, const StringView& hint_message) - { - const auto before = std::chrono::steady_clock::now(); - action(); - const auto after = std::chrono::steady_clock::now(); - const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(after - before); - DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count())); - } - - template<typename TReturn> - TReturn DebugTime(const std::function<TReturn()>& action, const StringView& hint_message) - { - const auto before = std::chrono::steady_clock::now(); - auto&& result = action(); - const auto after = std::chrono::steady_clock::now(); - const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(after - before); - DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count())); - return std::move(result); - } -#else - inline void DebugTime(const std::function<void()>& action, const StringView& hint_message) - { - action(); - } - - template<typename TReturn> - TReturn DebugTime(const std::function<TReturn()>& action, const StringView& hint_message) - { - return action(); - } -#endif -} -//-------------------------------------------------------- -//-------end of file: src\cru_debug.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\linear_layout.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - -namespace cru::ui::controls -{ - // Min length of main side in layout params is of no meaning. - // All children will layout from start and redundant length is blank. - class LinearLayout : public MultiChildControl - { - public: - static constexpr auto control_type = L"LinearLayout"; - - enum class Orientation - { - Horizontal, - Vertical - }; - - static LinearLayout* Create(const Orientation orientation = Orientation::Vertical, const std::initializer_list<Control*>& children = std::initializer_list<Control*>()) - { - const auto linear_layout = new LinearLayout(orientation); - for (const auto control : children) - linear_layout->AddChild(control); - return linear_layout; - } - - protected: - explicit LinearLayout(Orientation orientation = Orientation::Vertical); - - public: - LinearLayout(const LinearLayout& other) = delete; - LinearLayout(LinearLayout&& other) = delete; - LinearLayout& operator=(const LinearLayout& other) = delete; - LinearLayout& operator=(LinearLayout&& other) = delete; - ~LinearLayout() override = default; - - StringView GetControlType() const override final; - - protected: - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override; - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override; - - private: - Orientation orientation_; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\linear_layout.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\text_block.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\text_control.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - -namespace cru::ui::controls -{ - class TextControl : public NoChildControl - { - protected: - TextControl( - const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format, - const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush - ); - public: - TextControl(const TextControl& other) = delete; - TextControl(TextControl&& other) = delete; - TextControl& operator=(const TextControl& other) = delete; - TextControl& operator=(TextControl&& other) = delete; - ~TextControl() override = default; - - String GetText() const - { - return text_; - } - - void SetText(const String& text); - - Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const - { - return brush_; - } - - void SetBrush(const Microsoft::WRL::ComPtr<ID2D1Brush>& brush); - - Microsoft::WRL::ComPtr<IDWriteTextFormat> GetTextFormat() const - { - return text_format_; - } - - void SetTextFormat(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& text_format); - - bool IsSelectable() const - { - return is_selectable_; - } - - std::optional<TextRange> GetSelectedRange() const - { - return selected_range_; - } - - void SetSelectedRange(std::optional<TextRange> text_range); - - void ClearSelection() - { - SetSelectedRange(std::nullopt); - } - - protected: - void SetSelectable(bool is_selectable); - - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo&) override final; - - virtual void RequestChangeCaretPosition(unsigned position); - - void OnRectChange(const Rect& old_rect, const Rect& new_rect) override; - - private: - void OnTextChangedCore(const String& old_text, const String& new_text); - - void RecreateTextLayout(); - - // param point is the mouse point relative to this control. - void UpdateCursor(const std::optional<Point>& point); - - private: - String text_; - - Microsoft::WRL::ComPtr<ID2D1Brush> brush_; - Microsoft::WRL::ComPtr<ID2D1Brush> selection_brush_; - Microsoft::WRL::ComPtr<IDWriteTextFormat> text_format_; - protected: - Microsoft::WRL::ComPtr<IDWriteTextLayout> text_layout_; - - private: - bool is_selectable_ = false; - - bool is_selecting_ = false; - unsigned mouse_down_position_ = 0; - std::optional<TextRange> selected_range_ = std::nullopt; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\text_control.hpp -//-------------------------------------------------------- - -namespace cru::ui::controls -{ - class TextBlock : public TextControl - { - public: - static constexpr auto control_type = L"TextBlock"; - - static TextBlock* Create(const String& text = L"") - { - const auto text_block = new TextBlock(); - text_block->SetText(text); - return text_block; - } - - protected: - TextBlock(); - public: - TextBlock(const TextBlock& other) = delete; - TextBlock(TextBlock&& other) = delete; - TextBlock& operator=(const TextBlock& other) = delete; - TextBlock& operator=(TextBlock&& other) = delete; - ~TextBlock() override = default; - - StringView GetControlType() const override final; - - using TextControl::SetSelectable; // Make this public. - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\text_block.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\toggle_button.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - -namespace cru::ui::controls -{ - class ToggleButton : public NoChildControl - { - public: - static constexpr auto control_type = L"ToggleButton"; - - static ToggleButton* Create() - { - return new ToggleButton(); - } - - protected: - ToggleButton(); - - public: - ToggleButton(const ToggleButton& other) = delete; - ToggleButton(ToggleButton&& other) = delete; - ToggleButton& operator=(const ToggleButton& other) = delete; - ToggleButton& operator=(ToggleButton&& other) = delete; - ~ToggleButton() override = default; - - StringView GetControlType() const override final; - - bool IsPointInside(const Point& point) override; - - bool GetState() const - { - return state_; - } - - void SetState(bool state); - - void Toggle(); - - Event<events::ToggleEventArgs> toggle_event; - - protected: - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo&) override; - - private: - bool state_ = false; - - float current_circle_position_; - - Microsoft::WRL::ComPtr<ID2D1RoundedRectangleGeometry> frame_path_; - Microsoft::WRL::ComPtr<ID2D1Brush> on_brush_; - Microsoft::WRL::ComPtr<ID2D1Brush> off_brush_; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\toggle_button.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\button.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <initializer_list> - - -namespace cru::ui::controls -{ - class Button : public SingleChildControl - { - public: - static constexpr auto control_type = L"Button"; - - static Button* Create(Control* child = nullptr) - { - const auto button = new Button(); - button->SetChild(child); - return button; - } - - protected: - Button(); - - public: - Button(const Button& other) = delete; - Button(Button&& other) = delete; - Button& operator=(const Button& other) = delete; - Button& operator=(Button&& other) = delete; - ~Button() override = default; - - StringView GetControlType() const override final; - - protected: - void OnMouseClickBegin(MouseButton button) override final; - void OnMouseClickEnd(MouseButton button) override final; - - private: - BorderProperty normal_border_; - BorderProperty pressed_border_; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\button.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\text_box.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - -namespace cru::ui::controls -{ - class TextBox : public TextControl - { - public: - static constexpr auto control_type = L"TextBox"; - - static TextBox* Create() - { - return new TextBox(); - } - - protected: - TextBox(); - public: - TextBox(const TextBox& other) = delete; - TextBox(TextBox&& other) = delete; - TextBox& operator=(const TextBox& other) = delete; - TextBox& operator=(TextBox&& other) = delete; - ~TextBox() override; - - StringView GetControlType() const override final; - - protected: - void RequestChangeCaretPosition(unsigned position) override final; - - private: - // return true if left - bool GetCaretSelectionSide() const; - void ShiftLeftSelectionRange(int count); - void ShiftRightSelectionRange(int count); - - private: - unsigned caret_position_ = 0; - std::optional<TimerTask> caret_timer_{}; - Microsoft::WRL::ComPtr<ID2D1Brush> caret_brush_; - bool is_caret_show_ = false; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\text_box.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\list_item.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <map> -#include <initializer_list> - - -namespace cru::ui::controls -{ - class ListItem : public SingleChildControl - { - public: - static constexpr auto control_type = L"ListItem"; - - enum class State - { - Normal, - Hover, - Select - }; - - private: - struct StateBrush - { - Microsoft::WRL::ComPtr<ID2D1Brush> border_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> fill_brush; - }; - - public: - static ListItem* Create(Control* child = nullptr) - { - const auto list_item = new ListItem(); - list_item->SetChild(child); - return list_item; - } - - private: - ListItem(); - public: - ListItem(const ListItem& other) = delete; - ListItem(ListItem&& other) = delete; - ListItem& operator=(const ListItem& other) = delete; - ListItem& operator=(ListItem&& other) = delete; - ~ListItem() override = default; - - StringView GetControlType() const override; - - State GetState() const - { - return state_; - } - - void SetState(State state); - - private: - State state_ = State::Normal; - std::map<State, StateBrush> brushes_{}; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\list_item.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\popup_menu.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <vector> -#include <utility> -#include <functional> - - -namespace cru::ui -{ - class Window; -} - -namespace cru::ui::controls -{ - using MenuItemInfo = std::pair<String, std::function<void()>>; - - Window* CreatePopupMenu(const Point& anchor, const std::vector<MenuItemInfo>& items, Window* parent = nullptr); -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\popup_menu.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\frame_layout.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <initializer_list> - - -namespace cru::ui::controls -{ - class FrameLayout : public MultiChildControl - { - public: - static constexpr auto control_type = L"FrameLayout"; - - static FrameLayout* Create(const std::initializer_list<Control*>& children = std::initializer_list<Control*>{}) - { - const auto layout = new FrameLayout(); - for (auto child : children) - layout->AddChild(child); - return layout; - } - - protected: - FrameLayout(); - public: - FrameLayout(const FrameLayout& other) = delete; - FrameLayout(FrameLayout&& other) = delete; - FrameLayout& operator=(const FrameLayout& other) = delete; - FrameLayout& operator=(FrameLayout&& other) = delete; - ~FrameLayout() override; - - StringView GetControlType() const override final; - - protected: - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override; - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\frame_layout.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\controls\scroll_control.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <optional> -#include <initializer_list> - - -namespace cru::ui::controls -{ - // Done: OnMeasureContent - // Done: OnLayoutContent - // Done: HitTest(no need) - // Done: Draw(no need) - // Done: API - // Done: ScrollBar - // Done: MouseEvent - class ScrollControl : public SingleChildControl - { - private: - struct ScrollBarInfo - { - Rect border = Rect(); - Rect bar = Rect(); - }; - - enum class Orientation - { - Horizontal, - Vertical - }; - - public: - enum class ScrollBarVisibility - { - None, - Auto, - Always - }; - - static ScrollControl* Create(Control* child = nullptr) - { - const auto control = new ScrollControl(true); - control->SetChild(child); - return control; - } - - static constexpr auto control_type = L"ScrollControl"; - - protected: - explicit ScrollControl(bool container); - public: - ScrollControl(const ScrollControl& other) = delete; - ScrollControl(ScrollControl&& other) = delete; - ScrollControl& operator=(const ScrollControl& other) = delete; - ScrollControl& operator=(ScrollControl&& other) = delete; - ~ScrollControl() override; - - StringView GetControlType() const override final; - - bool IsHorizontalScrollEnabled() const - { - return horizontal_scroll_enabled_; - } - - void SetHorizontalScrollEnabled(bool enable); - - bool IsVerticalScrollEnabled() const - { - return vertical_scroll_enabled_; - } - - void SetVerticalScrollEnabled(bool enable); - - - ScrollBarVisibility GetHorizontalScrollBarVisibility() const - { - return horizontal_scroll_bar_visibility_; - } - - void SetHorizontalScrollBarVisibility(ScrollBarVisibility visibility); - - ScrollBarVisibility GetVerticalScrollBarVisibility() const - { - return vertical_scroll_bar_visibility_; - } - - void SetVerticalScrollBarVisibility(ScrollBarVisibility visibility); - - float GetViewWidth() const - { - return view_width_; - } - - float GetViewHeight() const - { - return view_height_; - } - - float GetScrollOffsetX() const - { - return offset_x_; - } - - float GetScrollOffsetY() const - { - return offset_y_; - } - - // nullopt for not set. value is auto-coerced. - void SetScrollOffset(std::optional<float> x, std::optional<float> y); - - protected: - void SetViewWidth(float length); - void SetViewHeight(float length); - - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override final; - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override final; - - void OnRectChange(const Rect& old_rect, const Rect& new_rect) override; - - private: - void CoerceAndSetOffsets(float offset_x, float offset_y, bool update_children = true); - void UpdateScrollBarVisibility(); - void UpdateScrollBarBorderInfo(); - void UpdateScrollBarBarInfo(); - - private: - bool horizontal_scroll_enabled_ = true; - bool vertical_scroll_enabled_ = true; - - ScrollBarVisibility horizontal_scroll_bar_visibility_ = ScrollBarVisibility::Auto; - ScrollBarVisibility vertical_scroll_bar_visibility_ = ScrollBarVisibility::Auto; - - bool is_horizontal_scroll_bar_visible_ = false; - bool is_vertical_scroll_bar_visible_ = false; - - float offset_x_ = 0.0f; - float offset_y_ = 0.0f; - - float view_width_ = 0.0f; - float view_height_ = 0.0f; - - ScrollBarInfo horizontal_bar_info_; - ScrollBarInfo vertical_bar_info_; - - std::optional<Orientation> is_pressing_scroll_bar_ = std::nullopt; - float pressing_delta_ = 0.0f; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\controls\scroll_control.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\graph\graph.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <memory> -#include <functional> - - - -namespace cru::graph -{ - class GraphManager; - - //Represents a window render target. - class WindowRenderTarget : public Object - { - public: - WindowRenderTarget(GraphManager* graph_manager, HWND hwnd); - WindowRenderTarget(const WindowRenderTarget& other) = delete; - WindowRenderTarget(WindowRenderTarget&& other) = delete; - WindowRenderTarget& operator=(const WindowRenderTarget& other) = delete; - WindowRenderTarget& operator=(WindowRenderTarget&& other) = delete; - ~WindowRenderTarget() override; - - public: - //Get the graph manager that created the render target. - GraphManager* GetGraphManager() const - { - return graph_manager_; - } - - //Get the d2d device context. - inline Microsoft::WRL::ComPtr<ID2D1DeviceContext> GetD2DDeviceContext() const; - - //Get the target bitmap which can be set as the ID2D1DeviceContext's target. - Microsoft::WRL::ComPtr<ID2D1Bitmap1> GetTargetBitmap() const - { - return target_bitmap_; - } - - //Resize the underlying buffer. - void ResizeBuffer(int width, int height); - - //Set this render target as the d2d device context's target. - void SetAsTarget(); - - //Present the data of the underlying buffer to the window. - void Present(); - - private: - void CreateTargetBitmap(); - - private: - GraphManager* graph_manager_; - Microsoft::WRL::ComPtr<IDXGISwapChain1> dxgi_swap_chain_; - Microsoft::WRL::ComPtr<ID2D1Bitmap1> target_bitmap_; - }; - - struct Dpi - { - float x; - float y; - }; - - class GraphManager final : public Object - { - public: - static GraphManager* GetInstance(); - - private: - GraphManager(); - public: - GraphManager(const GraphManager& other) = delete; - GraphManager(GraphManager&& other) = delete; - GraphManager& operator=(const GraphManager& other) = delete; - GraphManager& operator=(GraphManager&& other) = delete; - ~GraphManager() override; - - public: - Microsoft::WRL::ComPtr<ID2D1Factory1> GetD2D1Factory() const - { - return d2d1_factory_; - } - - Microsoft::WRL::ComPtr<ID2D1DeviceContext> GetD2D1DeviceContext() const - { - return d2d1_device_context_; - } - - Microsoft::WRL::ComPtr<ID3D11Device> GetD3D11Device() const - { - return d3d11_device_; - } - - Microsoft::WRL::ComPtr<IDXGIFactory2> GetDxgiFactory() const - { - return dxgi_factory_; - } - - Microsoft::WRL::ComPtr<IDWriteFactory> GetDWriteFactory() const - { - return dwrite_factory_; - } - - - //Create a window render target with the HWND. - std::shared_ptr<WindowRenderTarget> CreateWindowRenderTarget(HWND hwnd); - - //Get the desktop dpi. - Dpi GetDpi() const; - - //Reload system metrics including desktop dpi. - void ReloadSystemMetrics(); - - Microsoft::WRL::ComPtr<IDWriteFontCollection> GetSystemFontCollection() const - { - return dwrite_system_font_collection_.Get(); - } - - private: - Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; - Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_device_context_; - Microsoft::WRL::ComPtr<ID2D1Factory1> d2d1_factory_; - Microsoft::WRL::ComPtr<ID2D1Device> d2d1_device_; - Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2d1_device_context_; - Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory_; - - Microsoft::WRL::ComPtr<IDWriteFactory> dwrite_factory_; - Microsoft::WRL::ComPtr<IDWriteFontCollection> dwrite_system_font_collection_; - }; - - inline int DipToPixelInternal(const float dip, const float dpi) - { - return static_cast<int>(dip * dpi / 96.0f); - } - - inline int DipToPixelX(const float dip_x) - { - return DipToPixelInternal(dip_x, GraphManager::GetInstance()->GetDpi().x); - } - - inline int DipToPixelY(const float dip_y) - { - return DipToPixelInternal(dip_y, GraphManager::GetInstance()->GetDpi().y); - } - - inline float DipToPixelInternal(const int pixel, const float dpi) - { - return static_cast<float>(pixel) * 96.0f / dpi; - } - - inline float PixelToDipX(const int pixel_x) - { - return DipToPixelInternal(pixel_x, GraphManager::GetInstance()->GetDpi().x); - } - - inline float PixelToDipY(const int pixel_y) - { - return DipToPixelInternal(pixel_y, GraphManager::GetInstance()->GetDpi().y); - } - - Microsoft::WRL::ComPtr<ID2D1DeviceContext> WindowRenderTarget::GetD2DDeviceContext() const - { - return graph_manager_->GetD2D1DeviceContext(); - } - - inline void WithTransform(ID2D1DeviceContext* device_context, const D2D1_MATRIX_3X2_F matrix, const std::function<void(ID2D1DeviceContext*)>& action) - { - D2D1_MATRIX_3X2_F old_transform; - device_context->GetTransform(&old_transform); - device_context->SetTransform(old_transform * matrix); - action(device_context); - device_context->SetTransform(old_transform); - } - - Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidColorBrush(const D2D1_COLOR_F& color); -} -//-------------------------------------------------------- -//-------end of file: src\graph\graph.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\ui_manager.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - - -namespace cru::graph -{ - class GraphManager; -} - -namespace cru::ui -{ - struct CaretInfo - { - std::chrono::milliseconds caret_blink_duration; - float half_caret_width; - }; - - - class PredefineResources : public Object - { - public: - explicit PredefineResources(graph::GraphManager* graph_manager); - PredefineResources(const PredefineResources& other) = delete; - PredefineResources(PredefineResources&& other) = delete; - PredefineResources& operator=(const PredefineResources& other) = delete; - PredefineResources& operator=(PredefineResources&& other) = delete; - ~PredefineResources() override = default; - - //region BorderProperty - Microsoft::WRL::ComPtr<ID2D1Brush> border_property_brush; - - //region Button - BorderProperty button_normal_border; - BorderProperty button_press_border; - - //region TextControl - Microsoft::WRL::ComPtr<ID2D1Brush> text_control_selection_brush; - - //region TextBox - BorderProperty text_box_border; - Microsoft::WRL::ComPtr<ID2D1Brush> text_box_text_brush; - Microsoft::WRL::ComPtr<IDWriteTextFormat> text_box_text_format; - Microsoft::WRL::ComPtr<ID2D1Brush> text_box_caret_brush; - - //region TextBlock - Microsoft::WRL::ComPtr<ID2D1Brush> text_block_text_brush; - Microsoft::WRL::ComPtr<IDWriteTextFormat> text_block_text_format; - - //region ToggleButton - Microsoft::WRL::ComPtr<ID2D1Brush> toggle_button_on_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> toggle_button_off_brush; - - //region ListItem - Microsoft::WRL::ComPtr<ID2D1Brush> list_item_normal_border_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> list_item_normal_fill_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> list_item_hover_border_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> list_item_hover_fill_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> list_item_select_border_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> list_item_select_fill_brush; - - //region ScrollControl - Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_background_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_border_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> scroll_bar_brush; - -#ifdef CRU_DEBUG_LAYOUT - //region debug - Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_out_border_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_margin_brush; - Microsoft::WRL::ComPtr<ID2D1Brush> debug_layout_padding_brush; -#endif - }; - - class UiManager : public Object - { - public: - static UiManager* GetInstance(); - private: - UiManager(); - public: - UiManager(const UiManager& other) = delete; - UiManager(UiManager&& other) = delete; - UiManager& operator=(const UiManager& other) = delete; - UiManager& operator=(UiManager&& other) = delete; - ~UiManager() override = default; - - CaretInfo GetCaretInfo() const - { - return caret_info_; - } - - const PredefineResources* GetPredefineResources() const - { - return &predefine_resources_; - } - - private: - CaretInfo caret_info_; - - PredefineResources predefine_resources_; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\ui_manager.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\convert_util.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - - - -namespace cru::ui -{ - inline D2D1_POINT_2F Convert(const Point& point) - { - return D2D1::Point2F(point.x, point.y); - } - - inline D2D1_RECT_F Convert(const Rect& rect) - { - return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, rect.top + rect.height); - } -} -//-------------------------------------------------------- -//-------end of file: src\ui\convert_util.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\math_util.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -// ReSharper disable once CppUnusedIncludeDirective -#include <type_traits> -#include <optional> - -namespace cru -{ - template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> - float Coerce(const T n, const std::optional<T> min, const std::optional<T> max) - { - if (min.has_value() && n < min.value()) - return min.value(); - if (max.has_value() && n > max.value()) - return max.value(); - return n; - } - - template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> - float Coerce(const T n, const T min, const T max) - { - if (n < min) - return min; - if (n > max) - return max; - return n; - } - - template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> - float Coerce(const T n, const std::nullopt_t, const std::optional<T> max) - { - if (max.has_value() && n > max.value()) - return max.value(); - return n; - } - - template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> - float Coerce(const T n, const std::optional<T> min, const std::nullopt_t) - { - if (min.has_value() && n < min.value()) - return min.value(); - return n; - } - - template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> - float Coerce(const T n, const std::nullopt_t, const T max) - { - if (n > max) - return max; - return n; - } - - template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> - float Coerce(const T n, const T min, const std::nullopt_t) - { - if (n < min) - return min; - return n; - } - - template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> - T AtLeast0(const T value) - { - return value < static_cast<T>(0) ? static_cast<T>(0) : value; - } -} -//-------------------------------------------------------- -//-------end of file: src\math_util.hpp -//-------------------------------------------------------- -//-------------------------------------------------------- -//-------begin of file: src\ui\animations\animation.hpp -//-------------------------------------------------------- - -// ReSharper disable once CppUnusedIncludeDirective - -#include <unordered_map> - - -namespace cru::ui::animations -{ - using AnimationTimeUnit = FloatSecond; - - struct IAnimationDelegate : virtual Interface - { - virtual void Cancel() = 0; - }; - - using AnimationDelegatePtr = std::shared_ptr<IAnimationDelegate>; - - using AnimationStepHandler = std::function<void(AnimationDelegatePtr, double)>; - using AnimationStartHandler = std::function<void(AnimationDelegatePtr)>; - using AnimationFinishHandler = std::function<void()>; - using AnimationCancelHandler = std::function<void()>; - - namespace details - { - class Animation; - using AnimationPtr = std::unique_ptr<Animation>; - - class AnimationInfo - { - public: - AnimationInfo(String tag, const AnimationTimeUnit duration) - : tag(std::move(tag)), - duration(duration) - { - - } - AnimationInfo(const AnimationInfo& other) = default; - AnimationInfo(AnimationInfo&& other) = default; - AnimationInfo& operator=(const AnimationInfo& other) = default; - AnimationInfo& operator=(AnimationInfo&& other) = default; - ~AnimationInfo() = default; - - String tag; - AnimationTimeUnit duration; - std::vector<AnimationStepHandler> step_handlers{}; - std::vector<AnimationStartHandler> start_handlers{}; - std::vector<AnimationFinishHandler> finish_handlers{}; - std::vector<AnimationCancelHandler> cancel_handlers{}; - }; - - class AnimationManager : public Object - { - public: - static AnimationManager* GetInstance(); - private: - AnimationManager(); - public: - AnimationManager(const AnimationManager& other) = delete; - AnimationManager(AnimationManager&& other) = delete; - AnimationManager& operator=(const AnimationManager& other) = delete; - AnimationManager& operator=(AnimationManager&& other) = delete; - ~AnimationManager() override; - - AnimationDelegatePtr CreateAnimation(AnimationInfo info); - void RemoveAnimation(const String& tag); - - private: - void SetTimer(); - void KillTimer(); - - private: - std::unordered_map<String, AnimationPtr> animations_; - std::optional<TimerTask> timer_; - }; - } - - class AnimationBuilder : public Object - { - public: - AnimationBuilder(String tag, const AnimationTimeUnit duration) - : info_(std::move(tag), duration) - { - - } - - AnimationBuilder& AddStepHandler(const AnimationStepHandler& handler) - { - CheckValid(); - info_.step_handlers.push_back(handler); - return *this; - } - - AnimationBuilder& AddStartHandler(const AnimationStartHandler& handler) - { - CheckValid(); - info_.start_handlers.push_back(handler); - return *this; - } - - AnimationBuilder& AddFinishHandler(const AnimationFinishHandler& handler) - { - CheckValid(); - info_.finish_handlers.push_back(handler); - return *this; - } - - AnimationBuilder& AddCancelHandler(const AnimationCancelHandler& handler) - { - CheckValid(); - info_.cancel_handlers.push_back(handler); - return *this; - } - - AnimationDelegatePtr Start(); - - private: - void CheckValid() const - { - if (!valid_) - throw std::runtime_error("The animation builder is invalid."); - } - - bool valid_ = true; - details::AnimationInfo info_; - }; -} -//-------------------------------------------------------- -//-------end of file: src\ui\animations\animation.hpp -//-------------------------------------------------------- @@ -12,22 +12,14 @@ Please using VS2017 open the `CruUI.sln` file in the root directory. ## structure of repository -- `CruUI.sln` is *Visual Studio* solution file of the repository which including two vc++ projects. +- `CruUI.sln` is *Visual Studio* solution file of the repository. - `CruUI.vcxproj` is a *Visual Studio* vc++ project file that includes the main code in `src/`. - - `CruUI-Generate/CruUI-Generate.vcxproj` is a *Visual Studio* vc++ project file that includes a header and a source generated from tools(see below). It is a project used to test that generated codes work well. - - `src/` contains the main codes including headers and sources. - `tools/` contains some codes of tools like code generators. - `tools/cppmerge` contains a *python* script program that merges all cpp headers and sources into one **single** header and source. -- `scripts/` contains some useful scripts. - - - `scripts/merge.py` is a *python* script that invoke the *cppmerge* tool to merge code in `src\` to `CruUI-Generate\cru_ui.h` and `CruUI-Generate\cru_ui.cpp`. - -- `CruUI-Generate/` contains codes and project file that used to test merged codes. - - `snippets/` contains useful snippets file for *Visual Studio*, you can import it. diff --git a/scripts/merge.py b/scripts/merge.py deleted file mode 100644 index 250dfecb..00000000 --- a/scripts/merge.py +++ /dev/null @@ -1,13 +0,0 @@ -import os -import os.path - -script_dir = os.path.dirname(os.path.realpath(__file__)) - -tool_path =os.path.relpath(os.path.join(script_dir, '../tools/cppmerge/main.py')) -input_dir = os.path.relpath(os.path.join(script_dir, '../src')) -output_path = os.path.relpath(os.path.join(script_dir, '../CruUI-Generate/cru_ui')) - -print('Input dir is {}'.format(input_dir)) -print('Output path is {}'.format(output_path)) - -os.system('python {} -o {} {}'.format(tool_path, output_path, input_dir)) diff --git a/tools/cppmerge/.vscode/launch.json b/tools/cppmerge/.vscode/launch.json deleted file mode 100644 index dcbc3389..00000000 --- a/tools/cppmerge/.vscode/launch.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Python", - "type": "python", - "request": "launch", - "program": "${workspaceFolder}/main.py", - "args": [ - "--output", - "cru_ui", - "../../src" - ], - "console": "integratedTerminal" - } - ] -}
\ No newline at end of file |