From 87689ccb5f7bde033d628ed2fef0daaf934d3624 Mon Sep 17 00:00:00 2001 From: Marcus Minhorst Date: Tue, 26 Aug 2025 22:05:52 -0400 Subject: [PATCH] Squashed 'libraries/ZWidget/' changes from cecd34301d..924b8e0e78 924b8e0e78 Fix scroll size issue f0344ed99b Add option to hide scrollbar when no scrolling is to be had f7b01d6625 Remove stray sdl 214d098353 Theme role improvements 82d0431ca5 Textedit highlight now follows theme 373ab3f4ca Added hex Colorf alias 5ea6d5a62a Reduced duplicate code a92e045b18 Simplified theme d076c9991c Expanded example e4556c97df Named colors 6d9300c742 Fix COM thread error 6499e5c523 Add null pointer checks f9d5724210 Fix x11 backend locking up Implement raw mouse input support (XInput 2.0) Fix x11 DPI scaling 4a86fb5056 XInitThreads must be called when using vulkan 8095090774 Add DPI scale to X11 backend 02fb328172 Mute a bunch of warnings f13c745a63 Fix typo 5689bf5edd Merge in changes from SurrealEngine Use C++20 Make SDL2 optional git-subtree-dir: libraries/ZWidget git-subtree-split: 924b8e0e786093708c0d73419a347f8e6ffe55d6 --- CMakeLists.txt | 33 +- example/example.cpp | 449 +++++++++++++------- example/picopng.cpp | 4 +- include/zwidget/core/colorf.h | 15 + include/zwidget/core/span_layout.h | 2 +- include/zwidget/core/theme.h | 16 + include/zwidget/widgets/textedit/textedit.h | 8 +- src/core/canvas.cpp | 9 +- src/core/pathfill.cpp | 2 +- src/core/picopng/picopng.cpp | 4 +- src/core/span_layout.cpp | 6 +- src/core/theme.cpp | 351 ++++++--------- src/core/timer.cpp | 2 +- src/core/truetypefont.cpp | 28 +- src/core/utf8reader.cpp | 6 +- src/widgets/dropdown/dropdown.cpp | 13 +- src/widgets/lineedit/lineedit.cpp | 25 +- src/widgets/listview/listview.cpp | 14 +- src/widgets/menubar/menubar.cpp | 4 +- src/widgets/scrollbar/scrollbar.cpp | 15 +- src/widgets/tabwidget/tabwidget.cpp | 4 +- src/widgets/textedit/textedit.cpp | 46 +- src/window/sdl2/sdl2_display_window.cpp | 3 +- src/window/win32/win32_open_file_dialog.cpp | 93 ++-- src/window/win32/win32_open_file_dialog.h | 1 + src/window/x11/x11_display_window.cpp | 258 +++++++++-- src/window/x11/x11_display_window.h | 11 +- 27 files changed, 889 insertions(+), 533 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2fdc8cf5..aa20facba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,14 @@ if (UNIX AND NOT APPLE) ) endif() +# SDL2 finding stuff +# Optional on all platforms +find_package(SDL2 QUIET) +if(NOT ${SDL2_FOUND}) + include(FindPkgConfig) + pkg_search_module(SDL2 sdl2) +endif() + set(ZWIDGET_SOURCES src/core/canvas.cpp src/core/font.cpp @@ -226,11 +234,12 @@ elseif(APPLE) set(ZWIDGET_DEFINES -DUNIX -D_UNIX) set(ZWIDGET_LINK_OPTIONS -pthread) else() - set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_SDL2_SOURCES} ${ZWIDGET_X11_SOURCES}) - set(ZWIDGET_LIBS ${CMAKE_DL_LIBS} -ldl -lX11) - set(ZWIDGET_DEFINES -DUNIX -D_UNIX -DUSE_SDL2 -DUSE_X11) + set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_X11_SOURCES}) + set(ZWIDGET_LIBS ${CMAKE_DL_LIBS} -lX11 -lXi) + set(ZWIDGET_DEFINES -DUNIX -D_UNIX -DUSE_X11) set(ZWIDGET_LINK_OPTIONS -pthread) if (DBUS_FOUND) + include_directories("/usr/lib64/dbus-1.0/include") # Bazzite (and probably Fedora 42) keeps the platform-specific dbus headers in this folder set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_DBUS_SOURCES}) set(ZWIDGET_INCLUDE_DIRS ${ZWIDGET_INCLUDE_DIRS} ${DBUS_INCLUDE_DIRS}) set(ZWIDGET_LIBS ${ZWIDGET_LIBS} ${DBUS_LDFLAGS}) @@ -244,10 +253,16 @@ else() endif() endif() +if(SDL2_FOUND) + set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_SDL2_SOURCES}) + set(ZWIDGET_LIBS ${ZWIDGET_LIBS} ${SDL2_LIBRARIES} -lSDL2) + set(ZWIDGET_DEFINES ${ZWIDGET_DEFINES} -DUSE_SDL2) +endif() + if(MSVC) - set(CXX_WARNING_FLAGS /W3) + set(CXX_WARNING_FLAGS /W3) else() - set(CXX_WARNING_FLAGS -Wall -Wpedantic) + set(CXX_WARNING_FLAGS -Wall -Wpedantic) endif() add_library(zwidget STATIC ${ZWIDGET_SOURCES} ${ZWIDGET_INCLUDES}) @@ -256,7 +271,7 @@ target_compile_definitions(zwidget PRIVATE ${ZWIDGET_DEFINES}) target_include_directories(zwidget PRIVATE ${ZWIDGET_INCLUDE_DIRS}) target_link_options(zwidget PRIVATE ${ZWIDGET_LINK_OPTIONS}) target_link_libraries(zwidget ${ZWIDGET_LIBS}) -set_target_properties(zwidget PROPERTIES CXX_STANDARD 17) +set_target_properties(zwidget PROPERTIES CXX_STANDARD 20) target_compile_options(zwidget PRIVATE ${CXX_WARNING_FLAGS}) if(MSVC) @@ -275,7 +290,7 @@ if(ZWIDGET_BUILD_EXAMPLE) example/picopng.cpp example/picopng.h ) - target_compile_options(zwidget_example PRIVATE ${CXX_WARNING_FLAGS}) + target_compile_options(zwidget_example PRIVATE ${CXX_WARNING_FLAGS}) add_custom_command( TARGET zwidget_example POST_BUILD @@ -296,10 +311,10 @@ if(ZWIDGET_BUILD_EXAMPLE) elseif(APPLE) target_link_libraries(zwidget_example PRIVATE "-framework Cocoa -framework OpenGL") else() - target_link_libraries(zwidget_example PRIVATE ${ZWIDGET_LIBS} SDL2::SDL2) + target_link_libraries(zwidget_example PRIVATE ${ZWIDGET_LIBS}) endif() - set_target_properties(zwidget_example PROPERTIES CXX_STANDARD 17) + set_target_properties(zwidget_example PROPERTIES CXX_STANDARD 20) if(MSVC) set_property(TARGET zwidget_example PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") diff --git a/example/example.cpp b/example/example.cpp index cd2b1b740..2f7747880 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -13,136 +13,29 @@ #include #include #include "picopng.h" +#include + +// ************************************************************ +// Prototypes +// ************************************************************ static std::vector ReadAllBytes(const std::string& filename); -class LauncherWindow : public Widget +class LauncherWindowTab1 : public Widget { public: - LauncherWindow() : Widget(nullptr, WidgetType::Window) - { - Logo = new ImageBox(this); - WelcomeLabel = new TextLabel(this); - VersionLabel = new TextLabel(this); - SelectLabel = new TextLabel(this); - GeneralLabel = new TextLabel(this); - ExtrasLabel = new TextLabel(this); - FullscreenCheckbox = new CheckboxLabel(this); - DisableAutoloadCheckbox = new CheckboxLabel(this); - DontAskAgainCheckbox = new CheckboxLabel(this); - LightsCheckbox = new CheckboxLabel(this); - BrightmapsCheckbox = new CheckboxLabel(this); - WidescreenCheckbox = new CheckboxLabel(this); - PlayButton = new PushButton(this); - ExitButton = new PushButton(this); - GamesList = new ListView(this); - Choices = new Dropdown(this); + LauncherWindowTab1(Widget parent); + void OnGeometryChanged() override; +private: + TextEdit* Text = nullptr; +}; - SetWindowBackground(Colorf::fromRgba8(51, 51, 51)); - SetWindowBorderColor(Colorf::fromRgba8(51, 51, 51)); - SetWindowCaptionColor(Colorf::fromRgba8(33, 33, 33)); - SetWindowCaptionTextColor(Colorf::fromRgba8(226, 223, 219)); - SetWindowTitle("VKDoom Launcher"); - - WelcomeLabel->SetText("Welcome to VKDoom"); - VersionLabel->SetText("Version 0xdeadbabe."); - SelectLabel->SetText("Select which game file (IWAD) to run."); - PlayButton->SetText("Play Game"); - ExitButton->SetText("Exit"); - - ExitButton->OnClick = []{ - DisplayWindow::ExitLoop(); - }; - - GeneralLabel->SetText("General"); - ExtrasLabel->SetText("Extra Graphics"); - FullscreenCheckbox->SetText("Fullscreen"); - DisableAutoloadCheckbox->SetText("Disable autoload"); - DontAskAgainCheckbox->SetText("Don't ask me again"); - LightsCheckbox->SetText("Lights"); - BrightmapsCheckbox->SetText("Brightmaps"); - WidescreenCheckbox->SetText("Widescreen"); - - Choices->SetMaxDisplayItems(2); - Choices->AddItem("First"); - Choices->AddItem("Second"); - Choices->AddItem("Third"); - Choices->AddItem("Fourth"); - Choices->AddItem("Fifth"); - Choices->AddItem("Sixth"); - - Choices->OnChanged = [this](int index) { - std::cout << "Selected " << index << ":" << Choices->GetItem(index) << std::endl; - }; - - try - { - auto filedata = ReadAllBytes("banner.png"); - std::vector pixels; - unsigned long width = 0, height = 0; - int result = decodePNG(pixels, width, height, (const unsigned char*)filedata.data(), filedata.size(), true); - if (result == 0) - { - Logo->SetImage(Image::Create(width, height, ImageFormat::R8G8B8A8, pixels.data())); - } - } - catch (...) - { - } - } - - void OnGeometryChanged() override - { - double y = 0.0; - - Logo->SetFrameGeometry(0.0, y, GetWidth(), Logo->GetPreferredHeight()); - y += Logo->GetPreferredHeight(); - - y += 10.0; - - WelcomeLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0, WelcomeLabel->GetPreferredHeight()); - y += WelcomeLabel->GetPreferredHeight(); - - VersionLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0, VersionLabel->GetPreferredHeight()); - y += VersionLabel->GetPreferredHeight(); - - y += 10.0; - - SelectLabel->SetFrameGeometry(20.0, y, GetWidth() - 40.0 - Choices->GetPreferredWidth(), SelectLabel->GetPreferredHeight()); - y += SelectLabel->GetPreferredHeight(); - - Choices->SetFrameGeometry(GetWidth() - 20.0 - Choices->GetPreferredWidth(), y-Choices->GetPreferredHeight(), Choices->GetPreferredWidth(), Choices->GetPreferredHeight()); - - double listViewTop = y + 10.0; - - y = GetHeight() - 15.0 - PlayButton->GetPreferredHeight(); - PlayButton->SetFrameGeometry(20.0, y, 120.0, PlayButton->GetPreferredHeight()); - ExitButton->SetFrameGeometry(GetWidth() - 20.0 - 120.0, y, 120.0, PlayButton->GetPreferredHeight()); - - y -= 20.0; - - double panelWidth = 150.0; - y -= DontAskAgainCheckbox->GetPreferredHeight(); - DontAskAgainCheckbox->SetFrameGeometry(20.0, y, 190.0, DontAskAgainCheckbox->GetPreferredHeight()); - WidescreenCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, WidescreenCheckbox->GetPreferredHeight()); - - y -= DisableAutoloadCheckbox->GetPreferredHeight(); - DisableAutoloadCheckbox->SetFrameGeometry(20.0, y, 190.0, DisableAutoloadCheckbox->GetPreferredHeight()); - BrightmapsCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, BrightmapsCheckbox->GetPreferredHeight()); - - y -= FullscreenCheckbox->GetPreferredHeight(); - FullscreenCheckbox->SetFrameGeometry(20.0, y, 190.0, FullscreenCheckbox->GetPreferredHeight()); - LightsCheckbox->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, LightsCheckbox->GetPreferredHeight()); - - y -= GeneralLabel->GetPreferredHeight(); - GeneralLabel->SetFrameGeometry(20.0, y, 190.0, GeneralLabel->GetPreferredHeight()); - ExtrasLabel->SetFrameGeometry(GetWidth() - 20.0 - panelWidth, y, panelWidth, ExtrasLabel->GetPreferredHeight()); - - double listViewBottom = y - 10.0; - GamesList->SetFrameGeometry(20.0, listViewTop, GetWidth() - 40.0, std::max(listViewBottom - listViewTop, 0.0)); - } - - ImageBox* Logo = nullptr; +class LauncherWindowTab2 : public Widget +{ +public: + LauncherWindowTab2(Widget parent); + void OnGeometryChanged() override; +private: TextLabel* WelcomeLabel = nullptr; TextLabel* VersionLabel = nullptr; TextLabel* SelectLabel = nullptr; @@ -154,13 +47,244 @@ public: CheckboxLabel* LightsCheckbox = nullptr; CheckboxLabel* BrightmapsCheckbox = nullptr; CheckboxLabel* WidescreenCheckbox = nullptr; - PushButton* PlayButton = nullptr; - PushButton* ExitButton = nullptr; ListView* GamesList = nullptr; - Dropdown* Choices = nullptr; }; -static std::vector ReadAllBytes(const std::string& filename); +class LauncherWindowTab3 : public Widget +{ +public: + LauncherWindowTab3(Widget parent); + void OnGeometryChanged() override; +private: + TextLabel* Label = nullptr; + Dropdown* Choices = nullptr; + PushButton* Popup = nullptr; +}; + +class LauncherWindow : public Widget +{ +public: + LauncherWindow(); +private: + void OnClose() override; + void OnGeometryChanged() override; + + ImageBox* Logo = nullptr; + TabWidget* Pages = nullptr; + PushButton* ExitButton = nullptr; + + LauncherWindowTab1* Tab1 = nullptr; + LauncherWindowTab2* Tab2 = nullptr; + LauncherWindowTab3* Tab3 = nullptr; +}; + +// ************************************************************ +// UI implementation +// ************************************************************ + +LauncherWindow::LauncherWindow(): Widget(nullptr, WidgetType::Window) +{ + SetWindowTitle("ZWidget Demo"); + + Logo = new ImageBox(this); + ExitButton = new PushButton(this); + Pages = new TabWidget(this); + + Tab1 = new LauncherWindowTab1(this); + Tab2 = new LauncherWindowTab2(this); + Tab3 = new LauncherWindowTab3(this); + + Pages->AddTab(Tab1, "Welcome"); + Pages->AddTab(Tab2, "VKDoom"); + Pages->AddTab(Tab3, "ZWidgets"); + + ExitButton->SetText("Exit"); + + ExitButton->OnClick = []{ + DisplayWindow::ExitLoop(); + }; + + try + { + auto filedata = ReadAllBytes("banner.png"); + std::vector pixels; + unsigned long width = 0, height = 0; + int result = decodePNG(pixels, width, height, (const unsigned char*)filedata.data(), filedata.size(), true); + if (result == 0) + { + Logo->SetImage(Image::Create(width, height, ImageFormat::R8G8B8A8, pixels.data())); + } + } + catch (...) + { + } +} + +void LauncherWindow::OnGeometryChanged() +{ + double y = 0, h; + + h = Logo->GetPreferredHeight(); + Logo->SetFrameGeometry(0, y, GetWidth(), h); + y += h; + + h = GetHeight() - y - ExitButton->GetPreferredHeight() - 40; + Pages->SetFrameGeometry(0, y, GetWidth(), h); + y += h + 20; + + ExitButton->SetFrameGeometry(GetWidth() - 20 - 120, y, 120, ExitButton->GetPreferredHeight()); +} + +void LauncherWindow::OnClose() +{ + DisplayWindow::ExitLoop(); +} + +LauncherWindowTab1::LauncherWindowTab1(Widget parent): Widget(nullptr) +{ + Text = new TextEdit(this); + + Text->SetText( + "Welcome to VKDoom\n\n" + "Click the tabs to look at other widgets\n\n" + "Also, this text is editable\n" + ); +} + +void LauncherWindowTab1::OnGeometryChanged() +{ + Text->SetFrameGeometry(0, 10, GetWidth(), GetHeight()); +} + +LauncherWindowTab2::LauncherWindowTab2(Widget parent): Widget(nullptr) +{ + WelcomeLabel = new TextLabel(this); + VersionLabel = new TextLabel(this); + SelectLabel = new TextLabel(this); + GeneralLabel = new TextLabel(this); + ExtrasLabel = new TextLabel(this); + FullscreenCheckbox = new CheckboxLabel(this); + DisableAutoloadCheckbox = new CheckboxLabel(this); + DontAskAgainCheckbox = new CheckboxLabel(this); + LightsCheckbox = new CheckboxLabel(this); + BrightmapsCheckbox = new CheckboxLabel(this); + WidescreenCheckbox = new CheckboxLabel(this); + GamesList = new ListView(this); + + WelcomeLabel->SetText("Welcome to VKDoom"); + VersionLabel->SetText("Version 0xdeadbabe."); + SelectLabel->SetText("Select which game file (IWAD) to run."); + + GamesList->AddItem("Doom"); + GamesList->AddItem("Doom 2: Electric Boogaloo"); + GamesList->AddItem("Doom 3D"); + GamesList->AddItem("Doom 4: The Quest for Peace"); + GamesList->AddItem("Doom on Ice"); + GamesList->AddItem("The Doom"); + GamesList->AddItem("Doom 2"); + + GeneralLabel->SetText("General"); + ExtrasLabel->SetText("Extra Graphics"); + FullscreenCheckbox->SetText("Fullscreen"); + DisableAutoloadCheckbox->SetText("Disable autoload"); + DontAskAgainCheckbox->SetText("Don't ask me again"); + LightsCheckbox->SetText("Lights"); + BrightmapsCheckbox->SetText("Brightmaps"); + WidescreenCheckbox->SetText("Widescreen"); +} + +void LauncherWindowTab2::OnGeometryChanged() +{ + double y = 0, h; + + h = WelcomeLabel->GetPreferredHeight(); + WelcomeLabel->SetFrameGeometry(20, y, GetWidth() - 40, h); + y += h; + + h = VersionLabel->GetPreferredHeight(); + VersionLabel->SetFrameGeometry(20, y, GetWidth() - 40, h); + y += h + 10; + + h = SelectLabel->GetPreferredHeight(); + SelectLabel->SetFrameGeometry(20, y, GetWidth() - 40, h); + y += h; + + double listViewTop = y + 10, listViewBottom; + + y = GetHeight(); + + h = DontAskAgainCheckbox->GetPreferredHeight(); + y -= h; + DontAskAgainCheckbox->SetFrameGeometry(20, y, 190, h); + WidescreenCheckbox->SetFrameGeometry(GetWidth() - 170, y, 150, WidescreenCheckbox->GetPreferredHeight()); + + h = DisableAutoloadCheckbox->GetPreferredHeight(); + y -= h; + DisableAutoloadCheckbox->SetFrameGeometry(20, y, 190, h); + BrightmapsCheckbox->SetFrameGeometry(GetWidth() - 170, y, 150, BrightmapsCheckbox->GetPreferredHeight()); + + h = FullscreenCheckbox->GetPreferredHeight(); + y -= h; + FullscreenCheckbox->SetFrameGeometry(20, y, 190, h); + LightsCheckbox->SetFrameGeometry(GetWidth() - 170, y, 150, LightsCheckbox->GetPreferredHeight()); + + h = GeneralLabel->GetPreferredHeight(); + y -= h; + GeneralLabel->SetFrameGeometry(20, y, 190, GeneralLabel->GetPreferredHeight()); + ExtrasLabel->SetFrameGeometry(GetWidth() - 170, y, 150, ExtrasLabel->GetPreferredHeight()); + + listViewBottom = y - 10; + GamesList->SetFrameGeometry(20, listViewTop, GetWidth() - 40, std::max(listViewBottom - listViewTop, 0)); +} + +LauncherWindowTab3::LauncherWindowTab3(Widget parent): Widget(nullptr) +{ + Label = new TextLabel(this); + Choices = new Dropdown(this); + Popup = new PushButton(this); + + Label->SetText("Oh my, even more widgets"); + Popup->SetText("Click me."); + + Choices->SetMaxDisplayItems(2); + Choices->AddItem("First"); + Choices->AddItem("Second"); + Choices->AddItem("Third"); + Choices->AddItem("Fourth"); + Choices->AddItem("Fifth"); + Choices->AddItem("Sixth"); + + Choices->OnChanged = [this](int index) { + std::cout << "Selected " << index << ":" << Choices->GetItem(index) << std::endl; + }; + + Popup->OnClick = []{ + std::cout << "TODO: open popup" << std::endl; + }; +} + +void LauncherWindowTab3::OnGeometryChanged() +{ + double y = 0, h; + + y += 10; + + h = Label->GetPreferredHeight(); + Label->SetFrameGeometry(20, y, GetWidth() - 40, h); + y += h + 10; + + h = Choices->GetPreferredHeight(); + Choices->SetFrameGeometry(20, y, Choices->GetPreferredWidth(), h); + y += h + 10; + + h = Popup->GetPreferredHeight(); + Popup->SetFrameGeometry(20, y, 120, h); + y += h; +} + +// ************************************************************ +// Shared code +// ************************************************************ std::vector LoadWidgetFontData(const std::string& name) { @@ -178,9 +302,22 @@ enum class Backend { Default, Win32, SDL2, X11, Wayland }; -int example(Backend backend = Backend::Default) +enum class Theme { + Default, Light, Dark +}; + +int example( + Backend backend = Backend::Default, + Theme theme = Theme::Default +) { - WidgetTheme::SetTheme(std::make_unique()); + // just for testing themes + switch (theme) + { + case Theme::Default: WidgetTheme::SetTheme(std::make_unique()); break; + case Theme::Dark: WidgetTheme::SetTheme(std::make_unique()); break; + case Theme::Light: WidgetTheme::SetTheme(std::make_unique()); break; + } // just for testing backends switch (backend) @@ -192,38 +329,19 @@ int example(Backend backend = Backend::Default) case Backend::Wayland: DisplayBackend::Set(DisplayBackend::TryCreateWayland()); break; } -#if 1 auto launcher = new LauncherWindow(); launcher->SetFrameGeometry(100.0, 100.0, 615.0, 668.0); launcher->Show(); -#else - auto mainwindow = new MainWindow(); - auto textedit = new TextEdit(mainwindow); - textedit->SetText(R"( -#version 460 - -in vec4 AttrPos; -in vec4 AttrColor; -out vec4 Color; - -void main() -{ - gl_Position = AttrPos; - Color = AttrColor; -} -)"); - mainwindow->SetWindowTitle("ZWidget Example"); - mainwindow->SetFrameGeometry(100.0, 100.0, 1700.0, 900.0); - mainwindow->SetCentralWidget(textedit); - textedit->SetFocus(); - mainwindow->Show(); -#endif DisplayWindow::RunLoop(); return 0; } +// ************************************************************ +// Platform-specific code +// ************************************************************ + #ifdef WIN32 #include @@ -305,18 +423,25 @@ static std::vector ReadAllBytes(const std::string& filename) int main(int argc, const char** argv) { - std::string backendStr = argc > 1? argv[1]: ""; - std::transform(backendStr.begin(), backendStr.end(), backendStr.begin(), - [](unsigned char c){ return std::tolower(c); }); - Backend backend = Backend::Default; + Theme theme = Theme::Default; - if (backendStr == "sdl2") backend = Backend::SDL2; - if (backendStr == "x11") backend = Backend::X11; - if (backendStr == "wayland") backend = Backend::Wayland; - if (backendStr == "win32") backend = Backend::Win32; // lol + for (auto i = 1; i < argc; i++) + { + std::string s = argv[i]; + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c){ return std::tolower(c); }); - example(backend); + if (s == "light") { theme = Theme::Light; continue; } + if (s == "dark") { theme = Theme::Dark; continue; } + + if (s == "sdl2") { backend = Backend::SDL2; continue; } + if (s == "x11") { backend = Backend::X11; continue; } + if (s == "wayland") { backend = Backend::Wayland; continue; } + if (s == "win32") { backend = Backend::Win32; continue; } // lol + } + + example(backend, theme); } #endif diff --git a/example/picopng.cpp b/example/picopng.cpp index c2943eb14..7d3e910f2 100644 --- a/example/picopng.cpp +++ b/example/picopng.cpp @@ -260,7 +260,7 @@ int decodePNG(std::vector& out_image, unsigned long& image_width, readPngHeader(&in[0], size); if(error) return; size_t pos = 33; //first byte of the first chunk after the header std::vector idat; //the data from idat chunks - bool IEND = false, known_type = true; + bool IEND = false/*, known_type = true*/; info.key_defined = false; while(!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer { @@ -312,7 +312,7 @@ int decodePNG(std::vector& out_image, unsigned long& image_width, { if(!(in[pos + 0] & 32)) { error = 69; return; } //error: unknown critical chunk (5th bit of first byte of chunk type is 0) pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk - known_type = false; + //known_type = false; } pos += 4; //step over CRC (which is ignored) } diff --git a/include/zwidget/core/colorf.h b/include/zwidget/core/colorf.h index 935214799..ed3793629 100644 --- a/include/zwidget/core/colorf.h +++ b/include/zwidget/core/colorf.h @@ -17,6 +17,21 @@ public: return { r * s, g * s, b * s, a * s }; } + static Colorf fromRgba(uint32_t rgba) + { + return fromRgba8( + 0xff & rgba>>24, + 0xff & rgba>>16, + 0xff & rgba>>8, + 0xff & rgba + ); + } + + static Colorf fromRgb(uint32_t rgb) + { + return fromRgba(rgb<<8 | 0xff); + } + uint32_t toBgra8() const { uint32_t cr = (int)(std::max(std::min(r * 255.0f, 255.0f), 0.0f)); diff --git a/include/zwidget/core/span_layout.h b/include/zwidget/core/span_layout.h index 974feca40..5f6a15866 100644 --- a/include/zwidget/core/span_layout.h +++ b/include/zwidget/core/span_layout.h @@ -212,7 +212,7 @@ private: Colorf cursor_color; std::string::size_type sel_start = 0, sel_end = 0; - Colorf sel_foreground, sel_background = Colorf::fromRgba8(153, 201, 239); + Colorf sel_foreground, sel_background; std::string text; std::vector objects; diff --git a/include/zwidget/core/theme.h b/include/zwidget/core/theme.h index 5a5271bc3..04b1f7acb 100644 --- a/include/zwidget/core/theme.h +++ b/include/zwidget/core/theme.h @@ -55,7 +55,23 @@ public: class WidgetTheme { + struct SimpleTheme { + const Colorf bgMain; // background + const Colorf fgMain; // + const Colorf bgLight; // headers / inputs + const Colorf fgLight; // + const Colorf bgAction; // interactive elements + const Colorf fgAction; // + const Colorf bgHover; // hover / highlight + const Colorf fgHover; // + const Colorf bgActive; // click + const Colorf fgActive; // + const Colorf border; // around elements + const Colorf divider; // between elements + }; public: + WidgetTheme() {} + WidgetTheme(const struct SimpleTheme &theme); virtual ~WidgetTheme() = default; WidgetStyle* RegisterStyle(std::unique_ptr widgetStyle, const std::string& widgetClass); diff --git a/include/zwidget/widgets/textedit/textedit.h b/include/zwidget/widgets/textedit/textedit.h index f392f85d6..007e7bee3 100644 --- a/include/zwidget/widgets/textedit/textedit.h +++ b/include/zwidget/widgets/textedit/textedit.h @@ -83,6 +83,11 @@ private: SpanLayout layout; Rect box; bool invalidated = true; + + Line(const TextEdit *self) + { + layout.SetSelectionColors(self->selectionFG, self->selectionBG); + } }; struct ivec2 @@ -96,9 +101,10 @@ private: bool operator!=(const ivec2& b) const { return x != b.x || y != b.y; } }; + Colorf selectionBG, selectionFG; Scrollbar* vert_scrollbar; Timer* timer = nullptr; - std::vector lines = { Line() }; + std::vector lines = { Line{this} }; ivec2 cursor_pos = { 0, 0 }; int max_length = -1; bool mouse_selecting = false; diff --git a/src/core/canvas.cpp b/src/core/canvas.cpp index bc72b8f19..060e9366d 100644 --- a/src/core/canvas.cpp +++ b/src/core/canvas.cpp @@ -549,9 +549,7 @@ void BitmapCanvas::plot(float x, float y, float alpha, const Colorf& color) int xx = (int)x; int yy = (int)y; - int dwidth = width; - int dheight = height; - uint32_t* dest = pixels.data() + xx + yy * dwidth; + uint32_t* dest = pixels.data() + xx + yy * width; uint32_t cred = (int32_t)clamp(color.r * 256.0f, 0.0f, 256.0f); uint32_t cgreen = (int32_t)clamp(color.g * 256.0f, 0.0f, 256.0f); @@ -665,7 +663,6 @@ void BitmapCanvas::fillTile(float left, float top, float width, float height, Co return; int dwidth = this->width; - int dheight = this->height; uint32_t* dest = this->pixels.data(); int x0 = (int)left; @@ -771,11 +768,9 @@ void BitmapCanvas::drawTile(CanvasTexture* tex, float left, float top, float wid auto texture = static_cast(tex); int swidth = texture->Width; - int sheight = texture->Height; const uint32_t* src = texture->Data.data(); int dwidth = this->width; - int dheight = this->height; uint32_t* dest = this->pixels.data(); int x0 = (int)left; @@ -880,11 +875,9 @@ void BitmapCanvas::drawGlyph(CanvasTexture* tex, float left, float top, float wi auto texture = static_cast(tex); int swidth = texture->Width; - int sheight = texture->Height; const uint32_t* src = texture->Data.data(); int dwidth = this->width; - int dheight = this->height; uint32_t* dest = this->pixels.data(); int x0 = (int)left; diff --git a/src/core/pathfill.cpp b/src/core/pathfill.cpp index cb604ef3f..1d92a61e4 100644 --- a/src/core/pathfill.cpp +++ b/src/core/pathfill.cpp @@ -255,7 +255,7 @@ PathFillRasterizer::Extent PathFillRasterizer::FindExtent(const PathScanline* sc void PathFillRasterizer::Clear() { - for (size_t y = first_scanline; y < last_scanline; y++) + for (int y = first_scanline; y < last_scanline; y++) { auto& scanline = scanlines[y]; if (!scanline.edges.empty()) diff --git a/src/core/picopng/picopng.cpp b/src/core/picopng/picopng.cpp index 97eb3b500..e2ee1923c 100644 --- a/src/core/picopng/picopng.cpp +++ b/src/core/picopng/picopng.cpp @@ -262,7 +262,7 @@ int decodePNG(std::vector& out_image, unsigned long& image_width, readPngHeader(&in[0], size); if(error) return; size_t pos = 33; //first byte of the first chunk after the header std::vector idat; //the data from idat chunks - bool IEND = false, known_type = true; + bool IEND = false/*, known_type = true*/; info.key_defined = false; while(!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer { @@ -314,7 +314,7 @@ int decodePNG(std::vector& out_image, unsigned long& image_width, { if(!(in[pos + 0] & 32)) { error = 69; return; } //error: unknown critical chunk (5th bit of first byte of chunk type is 0) pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk - known_type = false; + /*known_type = false;*/ } pos += 4; //step over CRC (which is ignored) } diff --git a/src/core/span_layout.cpp b/src/core/span_layout.cpp index 1b68f6ef7..1f1e9e6d9 100644 --- a/src/core/span_layout.cpp +++ b/src/core/span_layout.cpp @@ -72,16 +72,12 @@ void SpanLayout::DrawLayout(Canvas* canvas) LineSegment& segment = line.segments.back(); if (cursor_visible && segment.end <= cursor_pos) { - switch (segment.type) - { - case object_text: + if (segment.type == object_text) { double cursor_x = x + segment.x_position + canvas->measureText(segment.font, text.substr(segment.start, segment.end - segment.start)).width; double cursor_width = 1; canvas->fillRect(Rect::ltrb(cursor_x, y + line.ascender - segment.ascender, cursor_width, y + line.ascender + segment.descender), cursor_color); } - break; - } } } diff --git a/src/core/theme.cpp b/src/core/theme.cpp index 88e482706..f3a87dbce 100644 --- a/src/core/theme.cpp +++ b/src/core/theme.cpp @@ -141,12 +141,26 @@ WidgetTheme* WidgetTheme::GetTheme() return CurrentTheme.get(); } -///////////////////////////////////////////////////////////////////////////// - -DarkWidgetTheme::DarkWidgetTheme() +WidgetTheme::WidgetTheme(const struct SimpleTheme &theme) { + + auto bgMain = theme.bgMain; // background + auto fgMain = theme.fgMain; // + auto bgLight = theme.bgLight; // headers / inputs + auto fgLight = theme.fgLight; // + auto bgAction = theme.bgAction; // interactive elements + auto fgAction = theme.fgAction; // + auto bgHover = theme.bgHover; // hover / highlight + auto fgHover = theme.fgHover; // + auto bgActive = theme.bgActive; // click + auto fgActive = theme.fgActive; // + auto border = theme.border; // around elements + auto divider = theme.divider; // between elements + + auto none = Colorf::transparent(); + auto widget = RegisterStyle(std::make_unique(), "widget"); - auto textlabel = RegisterStyle(std::make_unique(widget), "textlabel"); + /*auto textlabel =*/ RegisterStyle(std::make_unique(widget), "textlabel"); auto pushbutton = RegisterStyle(std::make_unique(widget), "pushbutton"); auto lineedit = RegisterStyle(std::make_unique(widget), "lineedit"); auto textedit = RegisterStyle(std::make_unique(widget), "textedit"); @@ -167,272 +181,177 @@ DarkWidgetTheme::DarkWidgetTheme() auto statusbar = RegisterStyle(std::make_unique(widget), "statusbar"); widget->SetString("font-family", "NotoSans"); - widget->SetColor("color", Colorf::fromRgba8(226, 223, 219)); - widget->SetColor("window-background", Colorf::fromRgba8(51, 51, 51)); - widget->SetColor("window-border", Colorf::fromRgba8(51, 51, 51)); - widget->SetColor("window-caption-color", Colorf::fromRgba8(33, 33, 33)); - widget->SetColor("window-caption-text-color", Colorf::fromRgba8(226, 223, 219)); + widget->SetColor("color", fgMain); + widget->SetColor("window-background", bgMain); + widget->SetColor("window-border", bgMain); + widget->SetColor("window-caption-color", bgLight); + widget->SetColor("window-caption-text-color", fgLight); pushbutton->SetDouble("noncontent-left", 10.0); pushbutton->SetDouble("noncontent-top", 5.0); pushbutton->SetDouble("noncontent-right", 10.0); pushbutton->SetDouble("noncontent-bottom", 5.0); - pushbutton->SetColor("background-color", Colorf::fromRgba8(68, 68, 68)); - pushbutton->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100)); - pushbutton->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100)); - pushbutton->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100)); - pushbutton->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100)); - pushbutton->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78)); - pushbutton->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88)); + pushbutton->SetColor("color", fgAction); + pushbutton->SetColor("background-color", bgAction); + pushbutton->SetColor("border-left-color", border); + pushbutton->SetColor("border-top-color", border); + pushbutton->SetColor("border-right-color", border); + pushbutton->SetColor("border-bottom-color", border); + pushbutton->SetColor("hover", "color", fgHover); + pushbutton->SetColor("hover", "background-color", bgHover); + pushbutton->SetColor("down", "color", fgActive); + pushbutton->SetColor("down", "background-color", bgActive); lineedit->SetDouble("noncontent-left", 5.0); lineedit->SetDouble("noncontent-top", 3.0); lineedit->SetDouble("noncontent-right", 5.0); lineedit->SetDouble("noncontent-bottom", 3.0); - lineedit->SetColor("background-color", Colorf::fromRgba8(38, 38, 38)); - lineedit->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100)); - lineedit->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100)); - lineedit->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100)); - lineedit->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100)); - lineedit->SetColor("selection-color", Colorf::fromRgba8(100, 100, 100)); - lineedit->SetColor("no-focus-selection-color", Colorf::fromRgba8(68, 68, 68)); + lineedit->SetColor("color", fgLight); + lineedit->SetColor("background-color", bgLight); + lineedit->SetColor("border-left-color", border); + lineedit->SetColor("border-top-color", border); + lineedit->SetColor("border-right-color", border); + lineedit->SetColor("border-bottom-color", border); + lineedit->SetColor("selection-color", bgHover); + lineedit->SetColor("no-focus-selection-color", bgHover); textedit->SetDouble("noncontent-left", 8.0); textedit->SetDouble("noncontent-top", 8.0); textedit->SetDouble("noncontent-right", 8.0); textedit->SetDouble("noncontent-bottom", 8.0); - textedit->SetColor("background-color", Colorf::fromRgba8(38, 38, 38)); - textedit->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100)); - textedit->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100)); - textedit->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100)); - textedit->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100)); + textedit->SetColor("color", fgLight); + textedit->SetColor("background-color", bgLight); + textedit->SetColor("border-left-color", border); + textedit->SetColor("border-top-color", border); + textedit->SetColor("border-right-color", border); + textedit->SetColor("border-bottom-color", border); + textedit->SetColor("selection-color", bgHover); listview->SetDouble("noncontent-left", 10.0); listview->SetDouble("noncontent-top", 10.0); listview->SetDouble("noncontent-right", 3.0); listview->SetDouble("noncontent-bottom", 10.0); - listview->SetColor("background-color", Colorf::fromRgba8(38, 38, 38)); - listview->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100)); - listview->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100)); - listview->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100)); - listview->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100)); - listview->SetColor("selection-color", Colorf::fromRgba8(100, 100, 100)); + listview->SetColor("color", fgLight); + listview->SetColor("background-color", bgLight); + listview->SetColor("border-left-color", border); + listview->SetColor("border-top-color", border); + listview->SetColor("border-right-color", border); + listview->SetColor("border-bottom-color", border); + listview->SetColor("selection-color", bgHover); dropdown->SetDouble("noncontent-left", 5.0); dropdown->SetDouble("noncontent-top", 5.0); dropdown->SetDouble("noncontent-right", 5.0); dropdown->SetDouble("noncontent-bottom", 5.0); - dropdown->SetColor("background-color", Colorf::fromRgba8(38, 38, 38)); - dropdown->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100)); - dropdown->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100)); - dropdown->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100)); - dropdown->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100)); - dropdown->SetColor("selection-color", Colorf::fromRgba8(100, 100, 100)); - dropdown->SetColor("arrow-color", Colorf::fromRgba8(100, 100, 100)); + dropdown->SetColor("color", fgLight); + dropdown->SetColor("background-color", bgLight); + dropdown->SetColor("border-left-color", border); + dropdown->SetColor("border-top-color", border); + dropdown->SetColor("border-right-color", border); + dropdown->SetColor("border-bottom-color", border); + dropdown->SetColor("arrow-color", border); - scrollbar->SetColor("track-color", Colorf::fromRgba8(33, 33, 33)); - scrollbar->SetColor("thumb-color", Colorf::fromRgba8(58, 58, 58)); + scrollbar->SetColor("track-color", divider); + scrollbar->SetColor("thumb-color", border); tabbar->SetDouble("spacer-left", 20.0); tabbar->SetDouble("spacer-right", 20.0); - tabbar->SetColor("background-color", Colorf::fromRgba8(33, 33, 33)); + tabbar->SetColor("background-color", bgLight); tabbar_tab->SetDouble("noncontent-left", 15.0); tabbar_tab->SetDouble("noncontent-right", 15.0); tabbar_tab->SetDouble("noncontent-top", 1.0); tabbar_tab->SetDouble("noncontent-bottom", 1.0); - tabbar_tab->SetColor("background-color", Colorf::fromRgba8(38, 38, 38)); - tabbar_tab->SetColor("border-left-color", Colorf::fromRgba8(68, 68, 68)); - tabbar_tab->SetColor("border-top-color", Colorf::fromRgba8(68, 68, 68)); - tabbar_tab->SetColor("border-right-color", Colorf::fromRgba8(68, 68, 68)); - tabbar_tab->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100)); - tabbar_tab->SetColor("hover", "background-color", Colorf::fromRgba8(45, 45, 45)); - tabbar_tab->SetColor("active", "background-color", Colorf::fromRgba8(51, 51, 51)); - tabbar_tab->SetColor("active", "border-left-color", Colorf::fromRgba8(100, 100, 100)); - tabbar_tab->SetColor("active", "border-top-color", Colorf::fromRgba8(100, 100, 100)); - tabbar_tab->SetColor("active", "border-right-color", Colorf::fromRgba8(100, 100, 100)); - tabbar_tab->SetColor("active", "border-bottom-color", Colorf::transparent()); + tabbar_tab->SetColor("color", fgMain); + tabbar_tab->SetColor("background-color", bgMain); + tabbar_tab->SetColor("border-left-color", divider); + tabbar_tab->SetColor("border-top-color", divider); + tabbar_tab->SetColor("border-right-color", divider); + tabbar_tab->SetColor("border-bottom-color", border); + tabbar_tab->SetColor("hover", "color", fgAction); + tabbar_tab->SetColor("hover", "background-color", bgAction); + tabbar_tab->SetColor("active", "background-color", bgMain); + tabbar_tab->SetColor("active", "border-left-color", border); + tabbar_tab->SetColor("active", "border-top-color", border); + tabbar_tab->SetColor("active", "border-right-color", border); + tabbar_tab->SetColor("active", "border-bottom-color", none); tabbar_spacer->SetDouble("noncontent-bottom", 1.0); - tabbar_spacer->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100)); + tabbar_spacer->SetColor("border-bottom-color", border); tabwidget_stack->SetDouble("noncontent-left", 20.0); tabwidget_stack->SetDouble("noncontent-top", 5.0); tabwidget_stack->SetDouble("noncontent-right", 20.0); tabwidget_stack->SetDouble("noncontent-bottom", 5.0); - checkbox_label->SetColor("checked-outer-border-color", Colorf::fromRgba8(100, 100, 100)); - checkbox_label->SetColor("checked-inner-border-color", Colorf::fromRgba8(51, 51, 51)); - checkbox_label->SetColor("checked-color", Colorf::fromRgba8(226, 223, 219)); - checkbox_label->SetColor("unchecked-outer-border-color", Colorf::fromRgba8(99, 99, 99)); - checkbox_label->SetColor("unchecked-inner-border-color", Colorf::fromRgba8(51, 51, 51)); + checkbox_label->SetColor("checked-outer-border-color", border); + checkbox_label->SetColor("checked-inner-border-color", bgMain); + checkbox_label->SetColor("checked-color", fgMain); + checkbox_label->SetColor("unchecked-outer-border-color", border); + checkbox_label->SetColor("unchecked-inner-border-color", bgMain); - menubar->SetColor("background-color", Colorf::fromRgba8(33, 33, 33)); - toolbar->SetColor("background-color", Colorf::fromRgba8(33, 33, 33)); - statusbar->SetColor("background-color", Colorf::fromRgba8(33, 33, 33)); + menubar->SetColor("background-color", bgLight); + toolbar->SetColor("background-color", bgLight); + statusbar->SetColor("background-color", bgLight); - toolbarbutton->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78)); - toolbarbutton->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88)); + toolbarbutton->SetColor("hover", "color", fgHover); + toolbarbutton->SetColor("hover", "background-color", bgHover); + toolbarbutton->SetColor("down", "color", fgActive); + toolbarbutton->SetColor("down", "background-color", bgActive); - menubaritem->SetColor("color", Colorf::fromRgba8(226, 223, 219)); - menubaritem->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78)); - menubaritem->SetColor("hover", "color", Colorf::fromRgba8(0, 0, 0)); - menubaritem->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88)); - menubaritem->SetColor("down", "color", Colorf::fromRgba8(0, 0, 0)); + menubaritem->SetColor("color", fgMain); + menubaritem->SetColor("hover", "color", fgHover); + menubaritem->SetColor("hover", "background-color", bgHover); + menubaritem->SetColor("down", "color", fgActive); + menubaritem->SetColor("down", "background-color", bgActive); menu->SetDouble("noncontent-left", 5.0); menu->SetDouble("noncontent-top", 5.0); menu->SetDouble("noncontent-right", 5.0); menu->SetDouble("noncontent-bottom", 5.0); - menu->SetColor("border-left-color", Colorf::fromRgba8(100, 100, 100)); - menu->SetColor("border-top-color", Colorf::fromRgba8(100, 100, 100)); - menu->SetColor("border-right-color", Colorf::fromRgba8(100, 100, 100)); - menu->SetColor("border-bottom-color", Colorf::fromRgba8(100, 100, 100)); + menu->SetColor("color", fgMain); + menu->SetColor("background-color", bgMain); + menu->SetColor("border-left-color", border); + menu->SetColor("border-top-color", border); + menu->SetColor("border-right-color", border); + menu->SetColor("border-bottom-color", border); - menuitem->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78)); - menuitem->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88)); + menuitem->SetColor("hover", "color", fgHover); + menuitem->SetColor("hover", "background-color", bgHover); + menuitem->SetColor("down", "color", fgActive); + menuitem->SetColor("down", "background-color", bgActive); } ///////////////////////////////////////////////////////////////////////////// -LightWidgetTheme::LightWidgetTheme() -{ - auto widget = RegisterStyle(std::make_unique(), "widget"); - auto textlabel = RegisterStyle(std::make_unique(widget), "textlabel"); - auto pushbutton = RegisterStyle(std::make_unique(widget), "pushbutton"); - auto lineedit = RegisterStyle(std::make_unique(widget), "lineedit"); - auto textedit = RegisterStyle(std::make_unique(widget), "textedit"); - auto listview = RegisterStyle(std::make_unique(widget), "listview"); - auto dropdown = RegisterStyle(std::make_unique(widget), "dropdown"); - auto scrollbar = RegisterStyle(std::make_unique(widget), "scrollbar"); - auto tabbar = RegisterStyle(std::make_unique(widget), "tabbar"); - auto tabbar_tab = RegisterStyle(std::make_unique(widget), "tabbar-tab"); - auto tabbar_spacer = RegisterStyle(std::make_unique(widget), "tabbar-spacer"); - auto tabwidget_stack = RegisterStyle(std::make_unique(widget), "tabwidget-stack"); - auto checkbox_label = RegisterStyle(std::make_unique(widget), "checkbox-label"); - auto menubar = RegisterStyle(std::make_unique(widget), "menubar"); - auto menubaritem = RegisterStyle(std::make_unique(widget), "menubaritem"); - auto menu = RegisterStyle(std::make_unique(widget), "menu"); - auto menuitem = RegisterStyle(std::make_unique(widget), "menuitem"); +DarkWidgetTheme::DarkWidgetTheme(): WidgetTheme({ + Colorf::fromRgb(0x2A2A2A), // background + Colorf::fromRgb(0xE2DFDB), // + Colorf::fromRgb(0x212121), // headers / inputs + Colorf::fromRgb(0xE2DFDB), // + Colorf::fromRgb(0x444444), // interactive elements + Colorf::fromRgb(0xFFFFFF), // + Colorf::fromRgb(0xC83C00), // hover / highlight + Colorf::fromRgb(0xFFFFFF), // + Colorf::fromRgb(0xBBBBBB), // click + Colorf::fromRgb(0x000000), // + Colorf::fromRgb(0x646464), // around elements + Colorf::fromRgb(0x555555) // between elements +}) {}; - widget->SetString("font-family", "NotoSans"); - widget->SetColor("color", Colorf::fromRgba8(0, 0, 0)); - widget->SetColor("window-background", Colorf::fromRgba8(240, 240, 240)); - widget->SetColor("window-border", Colorf::fromRgba8(100, 100, 100)); - widget->SetColor("window-caption-color", Colorf::fromRgba8(70, 70, 70)); - widget->SetColor("window-caption-text-color", Colorf::fromRgba8(226, 223, 219)); +///////////////////////////////////////////////////////////////////////////// - pushbutton->SetDouble("noncontent-left", 10.0); - pushbutton->SetDouble("noncontent-top", 5.0); - pushbutton->SetDouble("noncontent-right", 10.0); - pushbutton->SetDouble("noncontent-bottom", 5.0); - pushbutton->SetColor("background-color", Colorf::fromRgba8(210, 210, 210)); - pushbutton->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155)); - pushbutton->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155)); - pushbutton->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155)); - pushbutton->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155)); - pushbutton->SetColor("hover", "background-color", Colorf::fromRgba8(200, 200, 200)); - pushbutton->SetColor("down", "background-color", Colorf::fromRgba8(190, 190, 190)); - - lineedit->SetDouble("noncontent-left", 5.0); - lineedit->SetDouble("noncontent-top", 3.0); - lineedit->SetDouble("noncontent-right", 5.0); - lineedit->SetDouble("noncontent-bottom", 3.0); - lineedit->SetColor("background-color", Colorf::fromRgba8(255, 255, 255)); - lineedit->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155)); - lineedit->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155)); - lineedit->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155)); - lineedit->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155)); - lineedit->SetColor("selection-color", Colorf::fromRgba8(210, 210, 255)); - lineedit->SetColor("no-focus-selection-color", Colorf::fromRgba8(240, 240, 255)); - - textedit->SetDouble("noncontent-left", 8.0); - textedit->SetDouble("noncontent-top", 8.0); - textedit->SetDouble("noncontent-right", 8.0); - textedit->SetDouble("noncontent-bottom", 8.0); - textedit->SetColor("background-color", Colorf::fromRgba8(255, 255, 255)); - textedit->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155)); - textedit->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155)); - textedit->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155)); - textedit->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155)); - - listview->SetDouble("noncontent-left", 10.0); - listview->SetDouble("noncontent-top", 10.0); - listview->SetDouble("noncontent-right", 3.0); - listview->SetDouble("noncontent-bottom", 10.0); - listview->SetColor("background-color", Colorf::fromRgba8(230, 230, 230)); - listview->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155)); - listview->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155)); - listview->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155)); - listview->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155)); - listview->SetColor("selection-color", Colorf::fromRgba8(200, 200, 200)); - - dropdown->SetDouble("noncontent-left", 5.0); - dropdown->SetDouble("noncontent-top", 5.0); - dropdown->SetDouble("noncontent-right", 5.0); - dropdown->SetDouble("noncontent-bottom", 5.0); - dropdown->SetColor("background-color", Colorf::fromRgba8(230, 230, 230)); - dropdown->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155)); - dropdown->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155)); - dropdown->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155)); - dropdown->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155)); - dropdown->SetColor("selection-color", Colorf::fromRgba8(200, 200, 200)); - dropdown->SetColor("arrow-color", Colorf::fromRgba8(200, 200, 200)); - - scrollbar->SetColor("track-color", Colorf::fromRgba8(210, 210, 220)); - scrollbar->SetColor("thumb-color", Colorf::fromRgba8(180, 180, 180)); - - tabbar->SetDouble("spacer-left", 20.0); - tabbar->SetDouble("spacer-right", 20.0); - tabbar->SetColor("background-color", Colorf::fromRgba8(210, 210, 210)); - - tabbar_tab->SetDouble("noncontent-left", 15.0); - tabbar_tab->SetDouble("noncontent-right", 15.0); - tabbar_tab->SetDouble("noncontent-top", 1.0); - tabbar_tab->SetDouble("noncontent-bottom", 1.0); - tabbar_tab->SetColor("background-color", Colorf::fromRgba8(220, 220, 220)); - tabbar_tab->SetColor("border-left-color", Colorf::fromRgba8(200, 200, 200)); - tabbar_tab->SetColor("border-top-color", Colorf::fromRgba8(200, 200, 200)); - tabbar_tab->SetColor("border-right-color", Colorf::fromRgba8(200, 200, 200)); - tabbar_tab->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155)); - tabbar_tab->SetColor("hover", "background-color", Colorf::fromRgba8(210, 210, 210)); - tabbar_tab->SetColor("active", "background-color", Colorf::fromRgba8(240, 240, 240)); - tabbar_tab->SetColor("active", "border-left-color", Colorf::fromRgba8(155, 155, 155)); - tabbar_tab->SetColor("active", "border-top-color", Colorf::fromRgba8(155, 155, 155)); - tabbar_tab->SetColor("active", "border-right-color", Colorf::fromRgba8(155, 155, 155)); - tabbar_tab->SetColor("active", "border-bottom-color", Colorf::transparent()); - - tabbar_spacer->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155)); - - tabwidget_stack->SetDouble("noncontent-left", 20.0); - tabwidget_stack->SetDouble("noncontent-top", 5.0); - tabwidget_stack->SetDouble("noncontent-right", 20.0); - tabwidget_stack->SetDouble("noncontent-bottom", 5.0); - - checkbox_label->SetColor("checked-outer-border-color", Colorf::fromRgba8(155, 155, 155)); - checkbox_label->SetColor("checked-inner-border-color", Colorf::fromRgba8(200, 200, 200)); - checkbox_label->SetColor("checked-color", Colorf::fromRgba8(50, 50, 50)); - checkbox_label->SetColor("unchecked-outer-border-color", Colorf::fromRgba8(156, 156, 156)); - checkbox_label->SetColor("unchecked-inner-border-color", Colorf::fromRgba8(200, 200, 200)); - - menubar->SetColor("background-color", Colorf::fromRgba8(70, 70, 70)); - - menubaritem->SetColor("color", Colorf::fromRgba8(226, 223, 219)); - menubaritem->SetColor("hover", "background-color", Colorf::fromRgba8(200, 200, 200)); - menubaritem->SetColor("hover", "color", Colorf::fromRgba8(0, 0, 0)); - menubaritem->SetColor("down", "background-color", Colorf::fromRgba8(190, 190, 190)); - menubaritem->SetColor("down", "color", Colorf::fromRgba8(0, 0, 0)); - - menu->SetDouble("noncontent-left", 5.0); - menu->SetDouble("noncontent-top", 5.0); - menu->SetDouble("noncontent-right", 5.0); - menu->SetDouble("noncontent-bottom", 5.0); - menu->SetColor("background-color", Colorf::fromRgba8(255, 255, 255)); - menu->SetColor("border-left-color", Colorf::fromRgba8(155, 155, 155)); - menu->SetColor("border-top-color", Colorf::fromRgba8(155, 155, 155)); - menu->SetColor("border-right-color", Colorf::fromRgba8(155, 155, 155)); - menu->SetColor("border-bottom-color", Colorf::fromRgba8(155, 155, 155)); - - menuitem->SetColor("hover", "background-color", Colorf::fromRgba8(200, 200, 200)); - menuitem->SetColor("down", "background-color", Colorf::fromRgba8(190, 190, 190)); -} +LightWidgetTheme::LightWidgetTheme(): WidgetTheme({ + Colorf::fromRgb(0xF0F0F0), // background + Colorf::fromRgb(0x191919), // + Colorf::fromRgb(0xFAFAFA), // headers / inputs + Colorf::fromRgb(0x191919), // + Colorf::fromRgb(0xC8C8C8), // interactive elements + Colorf::fromRgb(0x000000), // + Colorf::fromRgb(0xD2D2FF), // hover / highlight + Colorf::fromRgb(0x000000), // + Colorf::fromRgb(0xC7B4FF), // click + Colorf::fromRgb(0x000000), // + Colorf::fromRgb(0xA0A0A0), // around elements + Colorf::fromRgb(0xB9B9B9) // between elements +}) {}; diff --git a/src/core/timer.cpp b/src/core/timer.cpp index 5ebe2612f..ba62a5ab3 100644 --- a/src/core/timer.cpp +++ b/src/core/timer.cpp @@ -27,7 +27,7 @@ void Timer::Start(int timeoutMilliseconds, bool repeat) { Stop(); - TimerId = DisplayWindow::StartTimer(timeoutMilliseconds, [=]() { + TimerId = DisplayWindow::StartTimer(timeoutMilliseconds, [this,repeat]() { if (!repeat) Stop(); if (FuncExpired) diff --git a/src/core/truetypefont.cpp b/src/core/truetypefont.cpp index 757929e3e..d5886d499 100644 --- a/src/core/truetypefont.cpp +++ b/src/core/truetypefont.cpp @@ -303,10 +303,10 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos reader.Seek(loca.offsets[glyphIndex]); ttf_int16 numberOfContours = reader.ReadInt16(); - ttf_int16 xMin = reader.ReadInt16(); - ttf_int16 yMin = reader.ReadInt16(); - ttf_int16 xMax = reader.ReadInt16(); - ttf_int16 yMax = reader.ReadInt16(); + /*ttf_int16 xMin =*/ reader.ReadInt16(); + /*ttf_int16 yMin =*/ reader.ReadInt16(); + /*ttf_int16 xMax =*/ reader.ReadInt16(); + /*ttf_int16 yMax =*/ reader.ReadInt16(); if (numberOfContours > 0) // Simple glyph { @@ -450,7 +450,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos if (transform) { - for (int i = childPointsOffset; i < g.points.size(); i++) + for (int i = childPointsOffset; i < (int)g.points.size(); i++) { float x = g.points[i].x * mat2x2[0] + g.points[i].y * mat2x2[1]; float y = g.points[i].x * mat2x2[2] + g.points[i].y * mat2x2[3]; @@ -492,7 +492,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos dy = g.points[parentPointIndex].y - g.points[childPointIndex].y; } - for (int i = childPointsOffset; i < g.points.size(); i++) + for (int i = childPointsOffset; i < (int)g.points.size(); i++) { g.points[i].x += dx; g.points[i].y += dy; @@ -589,9 +589,9 @@ void TrueTypeFont::LoadCharacterMapEncoding(TrueTypeFileReader& reader) for (ttf_uint16 c = startCode; c <= endCode; c++) { int offset = idRangeOffset / 2 + (c - startCode) - ((int)subformat.segCount - i); - if (offset >= 0 && offset < subformat.glyphIdArray.size()) + if (offset >= 0 && offset < (int)subformat.glyphIdArray.size()) { - int glyphId = subformat.glyphIdArray[offset]; + ttf_uint32 glyphId = subformat.glyphIdArray[offset]; if (firstGlyph) { range.startGlyphID = glyphId; @@ -689,9 +689,9 @@ void TTF_CMapSubtable4::Load(TrueTypeFileReader& reader) language = reader.ReadUInt16(); segCount = reader.ReadUInt16() / 2; - ttf_uint16 searchRange = reader.ReadUInt16(); - ttf_uint16 entrySelector = reader.ReadUInt16(); - ttf_uint16 rangeShift = reader.ReadUInt16(); + /*ttf_uint16 searchRange =*/ reader.ReadUInt16(); + /*ttf_uint16 entrySelector =*/ reader.ReadUInt16(); + /*ttf_uint16 rangeShift =*/ reader.ReadUInt16(); endCode.reserve(segCount); startCode.reserve(segCount); @@ -746,9 +746,9 @@ void TTF_TableDirectory::Load(TrueTypeFileReader& reader) numTables = reader.ReadUInt16(); // opentype spec says we can't use these for security reasons, so we pretend they never was part of the header - ttf_uint16 searchRange = reader.ReadUInt16(); - ttf_uint16 entrySelector = reader.ReadUInt16(); - ttf_uint16 rangeShift = reader.ReadUInt16(); + /*ttf_uint16 searchRange =*/ reader.ReadUInt16(); + /*ttf_uint16 entrySelector =*/ reader.ReadUInt16(); + /*ttf_uint16 rangeShift =*/ reader.ReadUInt16(); for (ttf_uint16 i = 0; i < numTables; i++) { diff --git a/src/core/utf8reader.cpp b/src/core/utf8reader.cpp index 200da9fdc..64b9c06af 100644 --- a/src/core/utf8reader.cpp +++ b/src/core/utf8reader.cpp @@ -71,7 +71,7 @@ unsigned int UTF8Reader::character() else { unsigned int ucs4 = (data[current_position] & bitmask_leadbyte_for_utf8[trailing_bytes]); - for (std::string::size_type i = 0; i < trailing_bytes; i++) + for (std::string::size_type i = 0; i < (size_t)trailing_bytes; i++) { if ((data[current_position + 1 + i] & 0xC0) == 0x80) ucs4 = (ucs4 << 6) + (data[current_position + 1 + i] & 0x3f); @@ -94,7 +94,7 @@ std::string::size_type UTF8Reader::char_length() if (current_position + 1 + trailing_bytes > length) return 1; - for (std::string::size_type i = 0; i < trailing_bytes; i++) + for (std::string::size_type i = 0; i < (size_t)trailing_bytes; i++) { if ((data[current_position + 1 + i] & 0xC0) != 0x80) return 1; @@ -136,7 +136,7 @@ void UTF8Reader::move_to_leadbyte() lead_position--; int trailing_bytes = trailing_bytes_for_utf8[data[lead_position]]; - if (lead_position + trailing_bytes >= current_position) + if (lead_position + trailing_bytes >= (int)current_position) current_position = lead_position; } diff --git a/src/widgets/dropdown/dropdown.cpp b/src/widgets/dropdown/dropdown.cpp index c40d0c02f..ff7b1ebd6 100644 --- a/src/widgets/dropdown/dropdown.cpp +++ b/src/widgets/dropdown/dropdown.cpp @@ -14,7 +14,8 @@ Dropdown::Dropdown(Widget* parent) : Widget(parent) } DropdownList::DropdownList(Widget* parent, Dropdown* owner) : ListView(parent), owner(owner) -{} +{ +} void DropdownList::OnKeyDown(InputKey key) { @@ -124,7 +125,7 @@ void Dropdown::ClearItems() void Dropdown::SetSelectedItem(int index) { if (index < 0) index = 0; - if (index >= (int)items.size()) index = items.size() - 1; + if (index >= (int)items.size()) index = (int)items.size() - 1; if (selectedItem == index) return; @@ -270,7 +271,7 @@ void Dropdown::OnGeometryChanged() Point pos = MapTo(Window(), Point(0,0)); double width = GetWidth() + GetNoncontentLeft() + GetNoncontentRight(); - double innerH = GetDisplayItems() * 25.0 + 10.0; + double innerH = GetDisplayItems() * 20.0 + 20; double outerH = GetHeight(); pos.x -= GetNoncontentLeft(); @@ -311,14 +312,14 @@ bool Dropdown::OpenDropdown() listView->SetSelectedItem(selectedItem); - listView->OnActivated = [=]() { OnDropdownActivated(); }; - listView->OnChanged = [=](int index) { OnDropdownActivated(); }; + listView->OnActivated = [this]() { OnDropdownActivated(); }; + listView->OnChanged = [this](int index) { OnDropdownActivated(); }; listView->SetFrameGeometry( 0, 0, GetWidth() + GetNoncontentLeft() + GetNoncontentRight(), - GetDisplayItems() * 25.0 + 10.0 + GetDisplayItems() * 20.0 + 20 ); OnGeometryChanged(); diff --git a/src/widgets/lineedit/lineedit.cpp b/src/widgets/lineedit/lineedit.cpp index 336ac9882..445bb34cd 100644 --- a/src/widgets/lineedit/lineedit.cpp +++ b/src/widgets/lineedit/lineedit.cpp @@ -9,10 +9,10 @@ LineEdit::LineEdit(Widget* parent) : Widget(parent) SetStyleClass("lineedit"); timer = new Timer(this); - timer->FuncExpired = [=]() { OnTimerExpired(); }; + timer->FuncExpired = [this]() { OnTimerExpired(); }; scroll_timer = new Timer(this); - scroll_timer->FuncExpired = [=]() { OnScrollTimerExpired(); }; + scroll_timer->FuncExpired = [this]() { OnScrollTimerExpired(); }; SetCursor(StandardCursor::ibeam); } @@ -90,12 +90,16 @@ int LineEdit::GetCursorPos() const Size LineEdit::GetTextSize() { Canvas* canvas = GetCanvas(); + if (!canvas) + return Size(0.0, 0.0); return GetVisualTextSize(canvas); } Size LineEdit::GetTextSize(const std::string& str) { Canvas* canvas = GetCanvas(); + if (!canvas) + return Size(0.0, 0.0); return canvas->measureText(str).size(); } @@ -674,7 +678,7 @@ bool LineEdit::InsertText(int pos, const std::string& str) } // checking if insert exceeds max length - if (UTF8Reader::utf8_length(text) + UTF8Reader::utf8_length(str) > max_length) + if (UTF8Reader::utf8_length(text) + UTF8Reader::utf8_length(str) > (size_t)max_length) { return false; } @@ -757,6 +761,9 @@ int LineEdit::GetCharacterIndex(double mouse_x) } Canvas* canvas = GetCanvas(); + if (!canvas) + return 0; + UTF8Reader utf8_reader(text.data(), text.length()); int seek_start = clip_start_offset; @@ -808,7 +815,7 @@ void LineEdit::UpdateTextClipping() if (!canvas) return; - Size text_size = GetVisualTextSize(canvas, clip_start_offset, (int)text.size() - clip_start_offset); + // Size text_size = GetVisualTextSize(canvas, clip_start_offset, (int)text.size() - clip_start_offset); if (cursor_pos < clip_start_offset) clip_start_offset = cursor_pos; @@ -822,7 +829,7 @@ void LineEdit::UpdateTextClipping() utf8_reader.set_position(clip_start_offset); utf8_reader.next(); clip_start_offset = (int)utf8_reader.position(); - if (clip_start_offset == text.size()) + if (clip_start_offset == (int)text.size()) break; cursor_rect = GetCursorRect(); } @@ -837,7 +844,7 @@ void LineEdit::UpdateTextClipping() utf8_reader.set_position(midpoint); utf8_reader.move_to_leadbyte(); - if (midpoint != utf8_reader.position()) + if (midpoint != (int)utf8_reader.position()) utf8_reader.next(); midpoint = (int)utf8_reader.position(); @@ -862,6 +869,8 @@ void LineEdit::UpdateTextClipping() Rect LineEdit::GetCursorRect() { Canvas* canvas = GetCanvas(); + if (!canvas) + return Rect::xywh(0.0, 0.0, 0.0, 0.0); int substr_end = cursor_pos - clip_start_offset; if (substr_end < 0) @@ -890,6 +899,8 @@ Rect LineEdit::GetCursorRect() Rect LineEdit::GetSelectionRect() { Canvas* canvas = GetCanvas(); + if (!canvas) + return Rect::xywh(0.0, 0.0, 0.0, 0.0); // text before selection: @@ -949,6 +960,8 @@ void LineEdit::OnTimerExpired() void LineEdit::OnGeometryChanged() { Canvas* canvas = GetCanvas(); + if (!canvas) + return; vertical_text_align = canvas->verticalTextAlign(); diff --git a/src/widgets/listview/listview.cpp b/src/widgets/listview/listview.cpp index 1f771b3b7..263e789bb 100644 --- a/src/widgets/listview/listview.cpp +++ b/src/widgets/listview/listview.cpp @@ -6,7 +6,7 @@ ListView::ListView(Widget* parent) : Widget(parent) SetStyleClass("listview"); scrollbar = new Scrollbar(this); - scrollbar->FuncScroll = [=]() { OnScrollbarScroll(); }; + scrollbar->FuncScroll = [this]() { OnScrollbarScroll(); }; SetColumnWidths({ 0.0 }); } @@ -37,7 +37,7 @@ void ListView::SetColumnWidths(const std::vector& widths) void ListView::AddItem(const std::string& text, int index, int column) { - if (column < 0 || column >= columnwidths.size()) + if (column < 0 || column >= (int)columnwidths.size()) return; std::vector newEntry; @@ -47,10 +47,10 @@ void ListView::AddItem(const std::string& text, int index, int column) newEntry[column] = text; if (index >= 0) { - if (index >= items.size()) + if (index >= (int)items.size()) { newEntry[column] = ""; - while (items.size() < index) + while ((int)items.size() < index) items.push_back(newEntry); newEntry[column] = text; @@ -71,7 +71,7 @@ void ListView::AddItem(const std::string& text, int index, int column) void ListView::UpdateItem(const std::string& text, int index, int column) { - if (index < 0 || index >= items.size() || column < 0 || column >= columnwidths.size()) + if (index < 0 || index >= (int)items.size() || column < 0 || column >= (int)columnwidths.size()) return; items[index][column] = text; @@ -80,7 +80,7 @@ void ListView::UpdateItem(const std::string& text, int index, int column) void ListView::RemoveItem(int index) { - if (!items.size() || index >= items.size()) + if (!items.size() || index >= (int)items.size()) return; if (index < 0) @@ -102,7 +102,7 @@ void ListView::Activate() void ListView::SetSelectedItem(int index) { - if (selectedItem != index && index >= 0 && index < items.size()) + if (selectedItem != index && index >= 0 && index < (int)items.size()) { selectedItem = index; if (OnChanged) OnChanged(selectedItem); diff --git a/src/widgets/menubar/menubar.cpp b/src/widgets/menubar/menubar.cpp index 37353756f..0da588996 100644 --- a/src/widgets/menubar/menubar.cpp +++ b/src/widgets/menubar/menubar.cpp @@ -36,7 +36,7 @@ void Menubar::ShowMenu(MenubarItem* item) if (item->GetOpenCallback()) { openMenu = new Menu(this); - openMenu->onCloseMenu = [=]() { CloseMenu(); }; + openMenu->onCloseMenu = [this]() { CloseMenu(); }; item->GetOpenCallback()(openMenu); if (item->AlignRight) openMenu->SetRightPosition(item->MapToGlobal(Point(item->GetWidth(), item->GetHeight()))); @@ -215,7 +215,7 @@ void Menubar::OnKeyDown(InputKey key) ///////////////////////////////////////////////////////////////////////////// -MenubarItem::MenubarItem(Menubar* menubar, std::string text, bool alignRight) : Widget(menubar), menubar(menubar), text(text), AlignRight(alignRight) +MenubarItem::MenubarItem(Menubar* menubar, std::string text, bool alignRight) : Widget(menubar), AlignRight(alignRight), menubar(menubar), text(text) { SetStyleClass("menubaritem"); } diff --git a/src/widgets/scrollbar/scrollbar.cpp b/src/widgets/scrollbar/scrollbar.cpp index c4e29329a..20ccc444f 100644 --- a/src/widgets/scrollbar/scrollbar.cpp +++ b/src/widgets/scrollbar/scrollbar.cpp @@ -3,13 +3,16 @@ #include "core/colorf.h" #include +#define HIDE_SMALL_BAR false +#define HIDE_SMALL_THUMB true + Scrollbar::Scrollbar(Widget* parent) : Widget(parent) { SetStyleClass("scrollbar"); UpdatePartPositions(); mouse_down_timer = new Timer(this); - mouse_down_timer->FuncExpired = [=]() { OnTimerExpired(); }; + mouse_down_timer->FuncExpired = [this]() { OnTimerExpired(); }; } Scrollbar::~Scrollbar() @@ -295,7 +298,15 @@ void Scrollbar::OnPaint(Canvas* canvas) part_button_increment.render_box(canvas, rect_button_increment); */ - canvas->fillRect(Rect::shrink(Rect::xywh(0.0, 0.0, GetWidth(), GetHeight()), 4.0, 0.0, 4.0, 0.0), GetStyleColor("track-color")); + auto height = GetHeight(); + auto paint = rect_thumb.height < height; + + if (HIDE_SMALL_BAR && !paint) return; + + canvas->fillRect(Rect::shrink(Rect::xywh(0.0, 0.0, GetWidth(), height), 4.0, 0.0, 4.0, 0.0), GetStyleColor("track-color")); + + if (HIDE_SMALL_THUMB && !paint) return; + canvas->fillRect(Rect::shrink(rect_thumb, 4.0, 0.0, 4.0, 0.0), GetStyleColor("thumb-color")); } diff --git a/src/widgets/tabwidget/tabwidget.cpp b/src/widgets/tabwidget/tabwidget.cpp index ef935a9e8..c3fd0df51 100644 --- a/src/widgets/tabwidget/tabwidget.cpp +++ b/src/widgets/tabwidget/tabwidget.cpp @@ -9,7 +9,7 @@ TabWidget::TabWidget(Widget* parent) : Widget(parent) Bar = new TabBar(this); PageStack = new TabWidgetStack(this); - Bar->OnCurrentChanged = [=]() { OnBarCurrentChanged(); }; + Bar->OnCurrentChanged = [this]() { OnBarCurrentChanged(); }; } int TabWidget::AddTab(Widget* page, const std::string& label) @@ -127,7 +127,7 @@ int TabBar::AddTab(const std::shared_ptr& icon, const std::string& label) TabBarTab* tab = new TabBarTab(this); tab->SetIcon(icon); tab->SetText(label); - tab->OnClick = [=]() { OnTabClicked(tab); }; + tab->OnClick = [this,tab]() { OnTabClicked(tab); }; int pageIndex = (int)Tabs.size(); Tabs.push_back(tab); if (CurrentIndex == -1) diff --git a/src/widgets/textedit/textedit.cpp b/src/widgets/textedit/textedit.cpp index 2381332a1..b6ec196e8 100644 --- a/src/widgets/textedit/textedit.cpp +++ b/src/widgets/textedit/textedit.cpp @@ -13,11 +13,14 @@ TextEdit::TextEdit(Widget* parent) : Widget(parent) { SetStyleClass("textedit"); + selectionBG = GetStyleColor("selection-color"); + selectionFG = GetStyleColor("color"); + timer = new Timer(this); - timer->FuncExpired = [=]() { OnTimerExpired(); }; + timer->FuncExpired = [this]() { OnTimerExpired(); }; scroll_timer = new Timer(this); - scroll_timer->FuncExpired = [=]() { OnScrollTimerExpired(); }; + scroll_timer->FuncExpired = [this]() { OnScrollTimerExpired(); }; SetCursor(StandardCursor::ibeam); @@ -175,13 +178,13 @@ void TextEdit::SetText(const std::string& text) std::string::size_type end = text.find('\n'); while (end != std::string::npos) { - TextEdit::Line line; + TextEdit::Line line{this}; line.text = text.substr(start, end - start); lines.push_back(line); start = end + 1; end = text.find('\n', start); } - TextEdit::Line line; + TextEdit::Line line{this}; line.text = text.substr(start); lines.push_back(line); @@ -197,13 +200,13 @@ void TextEdit::AddText(const std::string& text) std::string::size_type end = text.find('\n'); while (end != std::string::npos) { - TextEdit::Line line; + TextEdit::Line line{this}; line.text = text.substr(start, end - start); lines.push_back(line); start = end + 1; end = text.find('\n', start); } - TextEdit::Line line; + TextEdit::Line line{this}; line.text = text.substr(start); lines.push_back(line); @@ -434,7 +437,7 @@ void TextEdit::OnKeyDown(InputKey key) if (GetKeyState(InputKey::Shift) && selection_length == 0) selection_start = cursor_pos; - if (cursor_pos.y < lines.size() - 1) + if (cursor_pos.y < (int)lines.size() - 1) { cursor_pos.y++; cursor_pos.x = std::min(lines[cursor_pos.y].text.size(), (size_t)cursor_pos.x); @@ -578,7 +581,7 @@ void TextEdit::OnLostFocus() void TextEdit::CreateComponents() { vert_scrollbar = new Scrollbar(this); - vert_scrollbar->FuncScroll = [=]() { OnVerticalScroll(); }; + vert_scrollbar->FuncScroll = [this]() { OnVerticalScroll(); }; vert_scrollbar->SetVisible(false); vert_scrollbar->SetVertical(); } @@ -714,10 +717,10 @@ TextEdit::ivec2 TextEdit::FindNextBreakCharacter(ivec2 search_start) if (search_start.x >= int(lines[search_start.y].text.size()) - 1) return ivec2(lines[search_start.y].text.size(), search_start.y); - int pos = lines[search_start.y].text.find_first_of(break_characters, search_start.x); + size_t pos = lines[search_start.y].text.find_first_of(break_characters, search_start.x); if (pos == std::string::npos) return ivec2(lines[search_start.y].text.size(), search_start.y); - return ivec2(pos, search_start.y); + return ivec2((int)pos, search_start.y); } TextEdit::ivec2 TextEdit::FindPreviousBreakCharacter(ivec2 search_start) @@ -725,10 +728,10 @@ TextEdit::ivec2 TextEdit::FindPreviousBreakCharacter(ivec2 search_start) search_start.x--; if (search_start.x <= 0) return ivec2(0, search_start.y); - int pos = lines[search_start.y].text.find_last_of(break_characters, search_start.x); + size_t pos = lines[search_start.y].text.find_last_of(break_characters, search_start.x); if (pos == std::string::npos) return ivec2(0, search_start.y); - return ivec2(pos, search_start.y); + return ivec2((int)pos, search_start.y); } void TextEdit::InsertText(ivec2 pos, const std::string& str) @@ -741,7 +744,7 @@ void TextEdit::InsertText(ivec2 pos, const std::string& str) } // checking if insert exceeds max length - if (ToOffset(ivec2(lines[lines.size() - 1].text.size(), lines.size() - 1)) + str.length() > max_length) + if (ToOffset(ivec2((int)lines[lines.size() - 1].text.size(), (int)lines.size() - 1)) + str.length() > (size_t)max_length) { return; } @@ -759,7 +762,7 @@ void TextEdit::InsertText(ivec2 pos, const std::string& str) pos.x += next_newline - start; - Line line; + Line line{this}; line.text = lines[pos.y].text.substr(pos.x); lines.insert(lines.begin() + pos.y + 1, line); lines[pos.y].text = lines[pos.y].text.substr(0, pos.x); @@ -836,9 +839,9 @@ void TextEdit::Del() lines[cursor_pos.y].invalidated = true; Update(); } - else if (cursor_pos.y + 1 < lines.size()) + else if (cursor_pos.y + 1 < (int)lines.size()) { - selection_start = ivec2(lines[cursor_pos.y].text.length(), cursor_pos.y); + selection_start = ivec2((int)lines[cursor_pos.y].text.length(), cursor_pos.y); selection_length = 1; DeleteSelectedText(); } @@ -904,7 +907,7 @@ bool TextEdit::InputMaskAcceptsInput(ivec2 cursor_pos, const std::string& str) std::string::size_type TextEdit::ToOffset(ivec2 pos) const { - if (pos.y < lines.size()) + if (pos.y < (int)lines.size()) { std::string::size_type offset = 0; for (int line = 0; line < pos.y; line++) @@ -927,7 +930,7 @@ std::string::size_type TextEdit::ToOffset(ivec2 pos) const TextEdit::ivec2 TextEdit::FromOffset(std::string::size_type offset) const { int line_offset = 0; - for (int line = 0; line < lines.size(); line++) + for (int line = 0; line < (int)lines.size(); line++) { if (offset <= line_offset + lines[line].text.size()) { @@ -984,9 +987,9 @@ void TextEdit::LayoutLines(Canvas* canvas) line.invalidated = false; } - if (sel_start != sel_end && sel_start.y <= i && sel_end.y >= i) + if (sel_start != sel_end && sel_start.y <= (int)i && sel_end.y >= (int)i) { - line.layout.SetSelectionRange(sel_start.y < i ? 0 : sel_start.x, sel_end.y > i ? line.text.size() : sel_end.x); + line.layout.SetSelectionRange(sel_start.y < (int)i ? 0 : sel_start.x, sel_end.y > (int)i ? line.text.size() : sel_end.x); } else { @@ -996,7 +999,7 @@ void TextEdit::LayoutLines(Canvas* canvas) line.layout.HideCursor(); if (HasFocus()) { - if (cursor_blink_visible && cursor_pos.y == i) + if (cursor_blink_visible && cursor_pos.y == (int)i) { line.layout.SetCursorPos(cursor_pos.x); line.layout.SetCursorColor(textColor); @@ -1035,6 +1038,7 @@ TextEdit::ivec2 TextEdit::GetCharacterIndex(Point mouse_wincoords) return ivec2(clamp(result.offset, (size_t)0, line.text.size()), i); case SpanLayout::HitTestResult::outside_left: return ivec2(0, i); + default: case SpanLayout::HitTestResult::outside_right: return ivec2(line.text.size(), i); } diff --git a/src/window/sdl2/sdl2_display_window.cpp b/src/window/sdl2/sdl2_display_window.cpp index a71a4a1ca..27c644050 100644 --- a/src/window/sdl2/sdl2_display_window.cpp +++ b/src/window/sdl2/sdl2_display_window.cpp @@ -73,8 +73,7 @@ std::vector SDL2DisplayWindow::GetVulkanInstanceExtensions() VkSurfaceKHR SDL2DisplayWindow::CreateVulkanSurface(VkInstance instance) { VkSurfaceKHR surfaceHandle = {}; - SDL_Vulkan_CreateSurface(Handle.window, instance, &surfaceHandle); - if (surfaceHandle) + if (SDL_Vulkan_CreateSurface(Handle.window, instance, &surfaceHandle) != SDL_TRUE) throw std::runtime_error("Could not create vulkan surface"); return surfaceHandle; } diff --git a/src/window/win32/win32_open_file_dialog.cpp b/src/window/win32/win32_open_file_dialog.cpp index 092a78da1..ec2417b78 100644 --- a/src/window/win32/win32_open_file_dialog.cpp +++ b/src/window/win32/win32_open_file_dialog.cpp @@ -3,6 +3,7 @@ #include "win32_display_window.h" #include "core/widget.h" #include +#include #include #include #include @@ -14,6 +15,65 @@ Win32OpenFileDialog::Win32OpenFileDialog(Win32DisplayWindow* owner) : owner(owne bool Win32OpenFileDialog::Show() { + // For some reason this can hang deep inside Win32 if we do it on the calling thread! + bool done = false; + std::mutex mutex; + std::condition_variable condvar; + bool showResult = false; + std::exception_ptr exception; + + std::thread thread([&]() { + + try + { + showResult = ShowWorkerThread(); + } + catch (...) + { + exception = std::current_exception(); + } + + std::unique_lock lock(mutex); + done = true; + condvar.notify_all(); + + }); + + std::unique_lock lock(mutex); + while (!done) + { + DisplayBackend::Get()->ProcessEvents(); + using namespace std::chrono_literals; + condvar.wait_for(lock, 50ms, [&]() { return done; }); + } + lock.unlock(); + thread.join(); + + if (exception) + std::rethrow_exception(exception); + + return showResult; +} + +bool Win32OpenFileDialog::ShowWorkerThread() +{ + class InitCOM + { + public: + InitCOM() + { + HRESULT result = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + if (FAILED(result)) + throw std::runtime_error("CoInitializeEx(COINIT_APARTMENTTHREADED) failed"); + } + + ~InitCOM() + { + CoUninitialize(); + } + }; + InitCOM initCOM; + std::wstring title16 = to_utf16(title); std::wstring initial_directory16 = to_utf16(initial_directory); @@ -92,35 +152,10 @@ bool Win32OpenFileDialog::Show() } } - // For some reason this can hang deep inside Win32 if we do it on the calling thread! - { - bool done = false; - std::mutex mutex; - std::condition_variable condvar; - - std::thread thread([&]() { - - if (owner) - result = open_dialog->Show(owner->WindowHandle.hwnd); - else - result = open_dialog->Show(0); - - std::unique_lock lock(mutex); - done = true; - condvar.notify_all(); - - }); - - std::unique_lock lock(mutex); - while (!done) - { - DisplayBackend::Get()->ProcessEvents(); - using namespace std::chrono_literals; - condvar.wait_for(lock, 50ms, [&]() { return done; }); - } - lock.unlock(); - thread.join(); - } + if (owner) + result = open_dialog->Show(owner->WindowHandle.hwnd); + else + result = open_dialog->Show(0); if (SUCCEEDED(result)) { diff --git a/src/window/win32/win32_open_file_dialog.h b/src/window/win32/win32_open_file_dialog.h index 52ac858f0..a71e9ae9c 100644 --- a/src/window/win32/win32_open_file_dialog.h +++ b/src/window/win32/win32_open_file_dialog.h @@ -23,6 +23,7 @@ public: void SetDefaultExtension(const std::string& extension) override; private: + bool ShowWorkerThread(); void throw_if_failed(HRESULT result, const std::string& error); Win32DisplayWindow* owner = nullptr; diff --git a/src/window/x11/x11_display_window.cpp b/src/window/x11/x11_display_window.cpp index 00887ff94..756f34a2c 100644 --- a/src/window/x11/x11_display_window.cpp +++ b/src/window/x11/x11_display_window.cpp @@ -15,8 +15,8 @@ class X11Connection public: X11Connection() { - // If we ever want to support windows on multiple threads: - // XInitThreads(); + // This is required by vulkan + XInitThreads(); display = XOpenDisplay(nullptr); if (!display) @@ -35,6 +35,47 @@ public: XSetLocaleModifiers("@im=none"); xim = XOpenIM(display, 0, 0, 0); } + + // Look for XInput support + int event = 0, error = 0; + if (XQueryExtension(display, "XInputExtension", &XInputOpcode, &event, &error)) + { + // We need XInput 2.0 support + int major = 2, minor = 0; + if (XIQueryVersion(display, &major, &minor) != BadRequest) + { + // And we need a master pointer ID + int ndevices = 0; + XIDeviceInfo *devices = XIQueryDevice(display, XIAllDevices, &ndevices); + if (devices) + { + for (int i = 0; i < ndevices; i++) + { + if (devices[i].use == XIMasterPointer) + { + MasterPointerID = devices[i].deviceid; + XInput2Supported = true; + break; + } + } + XIFreeDeviceInfo(devices); + } + } + } + + // This XInput API is the gift that just keeps on giving. Instead of routing events to the + // focus window it sends it to the root window. Thanks! + if (XInput2Supported) + { + unsigned char mask[3] = { 0 }; + XISetMask(mask, XI_RawMotion); + XIEventMask eventmask; + eventmask.deviceid = MasterPointerID; + eventmask.mask_len = sizeof(mask); + eventmask.mask = mask; + Window root = XRootWindow(display, XDefaultScreen(display)); + XISelectEvents(display, root, &eventmask, 1); + } } ~X11Connection() @@ -53,6 +94,9 @@ public: bool ExitRunLoop = false; XIM xim = nullptr; + bool XInput2Supported = false; + int XInputOpcode = 0; + int MasterPointerID = 0; }; static X11Connection* GetX11Connection() @@ -75,18 +119,22 @@ static Atom GetAtom(const std::string& name) X11DisplayWindow::X11DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, X11DisplayWindow* owner, RenderAPI renderAPI) : windowHost(windowHost), owner(owner) { - display = GetX11Connection()->display; + auto connection = GetX11Connection(); + display = connection->display; screen = XDefaultScreen(display); depth = XDefaultDepth(display, screen); visual = XDefaultVisual(display, screen); colormap = XDefaultColormap(display, screen); - int disp_width_px = XDisplayWidth(display, screen); - int disp_height_px = XDisplayHeight(display, screen); - int disp_width_mm = XDisplayWidthMM(display, screen); - double ppi = (disp_width_mm < 24) ? 96.0 : (25.4 * static_cast(disp_width_px) / static_cast(disp_width_mm)); - dpiScale = std::round(ppi / 96.0 * 4.0) / 4.0; // 100%, 125%, 150%, 175%, 200%, etc. + if (char* value = XGetDefault(display, "Xft", "dpi")) + { + int dpi = std::atoi(value); + if (dpi != 0) + { + dpiScale = dpi / 96.0; + } + } XSetWindowAttributes attributes = {}; attributes.backing_store = Always; @@ -94,14 +142,32 @@ X11DisplayWindow::X11DisplayWindow(DisplayWindowHost* windowHost, bool popupWind attributes.save_under = popupWindow ? True : False; attributes.colormap = colormap; attributes.event_mask = - KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | - EnterWindowMask | LeaveWindowMask | PointerMotionMask | KeymapStateMask | + KeyPressMask | KeyReleaseMask | + EnterWindowMask | LeaveWindowMask | KeymapStateMask | ExposureMask | StructureNotifyMask | FocusChangeMask | PropertyChangeMask; + + if (!connection->XInput2Supported) + { + attributes.event_mask |= ButtonPressMask | ButtonReleaseMask | PointerMotionMask; + } unsigned long mask = CWBackingStore | CWSaveUnder | CWEventMask | CWOverrideRedirect; window = XCreateWindow(display, XRootWindow(display, screen), 0, 0, 100, 100, 0, depth, InputOutput, visual, mask, &attributes); - GetX11Connection()->windows[window] = this; + connection->windows[window] = this; + + if (connection->XInput2Supported) + { + unsigned char mask[1] = { 0 }; + XISetMask(mask, XI_ButtonPress); + XISetMask(mask, XI_ButtonRelease); + XISetMask(mask, XI_Motion); + XIEventMask eventmask; + eventmask.deviceid = connection->MasterPointerID; + eventmask.mask_len = sizeof(mask); + eventmask.mask = mask; + XISelectEvents(display, window, &eventmask, 1); + } if (owner) { @@ -158,10 +224,10 @@ X11DisplayWindow::X11DisplayWindow(DisplayWindowHost* windowHost, bool popupWind } // Create input context - if (GetX11Connection()->xim) + if (connection->xim) { xic = XCreateIC( - GetX11Connection()->xim, + connection->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window, @@ -378,7 +444,7 @@ Rect X11DisplayWindow::GetWindowFrame() const unsigned int height = 0; unsigned int borderwidth = 0; unsigned int depth = 0; - Status status = XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth); + /*Status status =*/ XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth); return Rect::xywh(x / dpiscale, y / dpiscale, width / dpiscale, height / dpiscale); } @@ -394,7 +460,7 @@ Size X11DisplayWindow::GetClientSize() const unsigned int height = 0; unsigned int borderwidth = 0; unsigned int depth = 0; - Status status = XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth); + /*Status status =*/ XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth); return Size(width / dpiscale, height / dpiscale); } @@ -408,7 +474,7 @@ int X11DisplayWindow::GetPixelWidth() const unsigned int height = 0; unsigned int borderwidth = 0; unsigned int depth = 0; - Status status = XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth); + /*Status status =*/ XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth); return width; } @@ -421,7 +487,7 @@ int X11DisplayWindow::GetPixelHeight() const unsigned int height = 0; unsigned int borderwidth = 0; unsigned int depth = 0; - Status status = XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth); + /*Status status =*/ XGetGeometry(display, window, &root, &x, &y, &width, &height, &borderwidth, &depth); return height; } @@ -486,8 +552,8 @@ void X11DisplayWindow::SetCaptionTextColor(uint32_t bgra8) std::vector X11DisplayWindow::GetWindowProperty(Atom property, Atom &actual_type, int &actual_format, unsigned long &item_count) { long read_bytes = 0; - Atom _actual_type = actual_type; - int _actual_format = actual_format; + //Atom _actual_type = actual_type; + //int _actual_format = actual_format; unsigned long _item_count = item_count; unsigned long bytes_remaining = 0; unsigned char *read_data = nullptr; @@ -565,7 +631,7 @@ Point X11DisplayWindow::MapFromGlobal(const Point& pos) const int srcy = (int)std::round(pos.y * dpiscale); int destx = 0; int desty = 0; - Bool result = XTranslateCoordinates(display, root, window, srcx, srcy, &destx, &desty, &child); + /*Bool result =*/ XTranslateCoordinates(display, root, window, srcx, srcy, &destx, &desty, &child); return Point(destx / dpiscale, desty / dpiscale); } @@ -578,7 +644,7 @@ Point X11DisplayWindow::MapToGlobal(const Point& pos) const int srcy = (int)std::round(pos.y * dpiscale); int destx = 0; int desty = 0; - Bool result = XTranslateCoordinates(display, window, root, srcx, srcy, &destx, &desty, &child); + /*Bool result =*/ XTranslateCoordinates(display, window, root, srcx, srcy, &destx, &desty, &child); return Point(destx / dpiscale, desty / dpiscale); } @@ -652,6 +718,23 @@ void X11DisplayWindow::ExitLoop() void X11DisplayWindow::DispatchEvent(XEvent* event) { X11Connection* connection = GetX11Connection(); + + if (connection->XInput2Supported) + { + // XInput sends all raw input to the root window. We want it routed to the focused window + if (event->xcookie.type == GenericEvent && + event->xcookie.extension == connection->XInputOpcode && + XGetEventData(connection->display, &event->xcookie)) + { + for (auto& it : connection->windows) + { + X11DisplayWindow* window = it.second; + window->OnXInputEvent(event); + } + XFreeEventData(connection->display, &event->xcookie); + } + } + auto it = connection->windows.find(event->xany.window); if (it != connection->windows.end()) { @@ -731,11 +814,13 @@ void X11DisplayWindow::OnFocusIn(XEvent* event) if (xic) XSetICFocus(xic); + RawInput.Focused = true; windowHost->OnWindowActivated(); } void X11DisplayWindow::OnFocusOut(XEvent* event) { + RawInput.Focused = false; windowHost->OnWindowDeactivated(); } @@ -949,6 +1034,8 @@ void X11DisplayWindow::OnKeyRelease(XEvent* event) void X11DisplayWindow::OnButtonPress(XEvent* event) { + // Note: this only gets called if XInput is not available + InputKey key = GetInputKey(event); keyState[key] = true; windowHost->OnWindowMouseDown(GetMousePos(event), key); @@ -958,6 +1045,8 @@ void X11DisplayWindow::OnButtonPress(XEvent* event) void X11DisplayWindow::OnButtonRelease(XEvent* event) { + // Note: this only gets called if XInput is not available + InputKey key = GetInputKey(event); keyState[key] = false; windowHost->OnWindowMouseUp(GetMousePos(event), key); @@ -965,6 +1054,8 @@ void X11DisplayWindow::OnButtonRelease(XEvent* event) void X11DisplayWindow::OnMotionNotify(XEvent* event) { + // Note: this only gets called if XInput is not available + double dpiScale = GetDpiScale(); int x = event->xmotion.x; int y = event->xmotion.y; @@ -974,18 +1065,127 @@ void X11DisplayWindow::OnMotionNotify(XEvent* event) } else { - MouseX = ClientSizeX / 2; - MouseY = ClientSizeY / 2; - - if (MouseX != -1 && MouseY != -1) + int dx = x - RawInput.LastX; + int dy = y - RawInput.LastY; + RawInput.LastX = x; + RawInput.LastY = y; + int centerX = ClientSizeX / 2; + int centerY = ClientSizeY / 2; + if (x != centerX || y != centerY) { - windowHost->OnWindowRawMouseMove(x - MouseX, y - MouseY); + // We still have to contain the mouse even if we are using XInput. + // Maybe we can do that in a simpler way if XInput is available? + XWarpPointer(display, window, window, 0, 0, ClientSizeX, ClientSizeY, centerX, centerY); + + // Use the accelerated mouse cursor if there's no XInput 2.0 support + if (!GetX11Connection()->XInput2Supported) + { + if (dx != 0 || dy != 0) + windowHost->OnWindowRawMouseMove(dx, dy); + } + } + } +} + +void X11DisplayWindow::OnXInputEvent(XEvent* event) +{ + // This API is so horrible it makes Win32 look attractive! + if (event->xcookie.evtype == XI_ButtonPress || event->xcookie.evtype == XI_ButtonRelease) + { + auto deviceEvent = (XIDeviceEvent*)event->xcookie.data; + if (deviceEvent->event != window) + return; + + InputKey key = {}; + switch (deviceEvent->detail) + { + case 1: key = InputKey::LeftMouse; break; + case 2: key = InputKey::MiddleMouse; break; + case 3: key = InputKey::RightMouse; break; + case 4: key = InputKey::MouseWheelUp; break; + case 5: key = InputKey::MouseWheelDown; break; + // case 6: key = InputKey::XButton1; break; + // case 7: key = InputKey::XButton2; break; + default: return; } - // Warp pointer to the center of the window - XWarpPointer(display, window, window, 0, 0, ClientSizeX, ClientSizeY, ClientSizeX / 2, ClientSizeY / 2); - } + double dpiScale = GetDpiScale(); + int x = (int)std::round(deviceEvent->event_x); + int y = (int)std::round(deviceEvent->event_y); + Point mousePos(x / dpiScale, y / dpiScale); + if (!isCursorEnabled) + { + // Raw input gets blocked until we ungrab the pointer. Worst design EVER. + XUngrabPointer(display, deviceEvent->time); + } + + if (event->xcookie.evtype == XI_ButtonPress) + { + keyState[key] = true; + windowHost->OnWindowMouseDown(mousePos, key); + // if (lastClickWithin400ms) + // windowHost->OnWindowMouseDoubleclick(GetMousePos(event), InputKey::LeftMouse); + } + else + { + keyState[key] = false; + windowHost->OnWindowMouseUp(mousePos, key); + } + } + else if (event->xcookie.evtype == XI_Motion) + { + auto deviceEvent = (XIDeviceEvent*)event->xcookie.data; + if (deviceEvent->event != window) + return; + + if (isCursorEnabled) + { + double dpiScale = GetDpiScale(); + int x = (int)std::round(deviceEvent->event_x); + int y = (int)std::round(deviceEvent->event_y); + Point mousePos(x / dpiScale, y / dpiScale); + windowHost->OnWindowMouseMove(Point(x / dpiScale, y / dpiScale)); + } + else if (RawInput.Focused) + { + int x = (int)std::round(deviceEvent->event_x); + int y = (int)std::round(deviceEvent->event_y); + int centerX = ClientSizeX / 2; + int centerY = ClientSizeY / 2; + if (x != centerX || y != centerY) + { + // We still have to contain the mouse even if we are using XInput. + // Maybe we can do that in a simpler way? + XWarpPointer(display, window, window, 0, 0, ClientSizeX, ClientSizeY, centerX, centerY); + } + } + } + else if (!isCursorEnabled && RawInput.Focused && event->xcookie.evtype == XI_RawMotion) + { + std::vector values; + values.reserve(2); + + auto rawEvent = (XIRawEvent*)event->xcookie.data; + double *rawValuator = rawEvent->raw_values; + for (int i = 0; i < rawEvent->valuators.mask_len * 8; i++) + { + if (XIMaskIsSet(rawEvent->valuators.mask, i)) + { + values.push_back(*rawValuator); + rawValuator++; + } + } + + if (values.size() >= 2) + { + // Values seems to be integers for my mouse. Is that always the case? + int dx = (int)std::round(values[0]); + int dy = (int)std::round(values[1]); + if (dx != 0 || dy != 0) + windowHost->OnWindowRawMouseMove(dx, dy); + } + } } void X11DisplayWindow::OnLeaveNotify(XEvent* event) diff --git a/src/window/x11/x11_display_window.h b/src/window/x11/x11_display_window.h index 62a63f151..174c95238 100644 --- a/src/window/x11/x11_display_window.h +++ b/src/window/x11/x11_display_window.h @@ -7,6 +7,7 @@ #include #include #include +#include #include class X11DisplayWindow : public DisplayWindow @@ -85,6 +86,7 @@ private: void OnSelectionClear(XEvent* event); void OnSelectionNotify(XEvent* event); void OnSelectionRequest(XEvent* event); + void OnXInputEvent(XEvent* event); void CreateBackbuffer(int width, int height); void DestroyBackbuffer(); @@ -117,8 +119,13 @@ private: int ClientSizeX = 0; int ClientSizeY = 0; - int MouseX = -1; - int MouseY = -1; + + struct + { + int LastX = -1; + int LastY = -1; + bool Focused = false; + } RawInput; Pixmap cursor_bitmap = None; Cursor hidden_cursor = None;