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;