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
This commit is contained in:
Marcus Minhorst 2025-08-26 22:05:52 -04:00
commit 87689ccb5f
27 changed files with 880 additions and 524 deletions

View file

@ -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$<$<CONFIG:Debug>:Debug>")

View file

@ -13,136 +13,29 @@
#include <zwidget/widgets/pushbutton/pushbutton.h>
#include <zwidget/widgets/checkboxlabel/checkboxlabel.h>
#include "picopng.h"
#include <zwidget/widgets/tabwidget/tabwidget.h>
// ************************************************************
// Prototypes
// ************************************************************
static std::vector<uint8_t> 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<unsigned char> 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<uint8_t> 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<unsigned char> 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<double>(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<SingleFontData> 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<DarkWidgetTheme>());
// just for testing themes
switch (theme)
{
case Theme::Default: WidgetTheme::SetTheme(std::make_unique<DarkWidgetTheme>()); break;
case Theme::Dark: WidgetTheme::SetTheme(std::make_unique<DarkWidgetTheme>()); break;
case Theme::Light: WidgetTheme::SetTheme(std::make_unique<LightWidgetTheme>()); 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 <Windows.h>
@ -305,18 +423,25 @@ static std::vector<uint8_t> 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

View file

@ -260,7 +260,7 @@ int decodePNG(std::vector<unsigned char>& 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<unsigned char> 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<unsigned char>& 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)
}

View file

@ -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));

View file

@ -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<SpanObject> objects;

View file

@ -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> widgetStyle, const std::string& widgetClass);

View file

@ -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<Line> lines = { Line() };
std::vector<Line> lines = { Line{this} };
ivec2 cursor_pos = { 0, 0 };
int max_length = -1;
bool mouse_selecting = false;

View file

@ -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<BitmapTexture*>(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<BitmapTexture*>(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;

View file

@ -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())

View file

@ -262,7 +262,7 @@ int decodePNG(std::vector<unsigned char>& 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<unsigned char> 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<unsigned char>& 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)
}

View file

@ -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;
}
}
}

View file

@ -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<BasicWidgetStyle>(), "widget");
auto textlabel = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textlabel");
/*auto textlabel =*/ RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textlabel");
auto pushbutton = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "pushbutton");
auto lineedit = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "lineedit");
auto textedit = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textedit");
@ -167,272 +181,177 @@ DarkWidgetTheme::DarkWidgetTheme()
auto statusbar = RegisterStyle(std::make_unique<BasicWidgetStyle>(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<BasicWidgetStyle>(), "widget");
auto textlabel = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textlabel");
auto pushbutton = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "pushbutton");
auto lineedit = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "lineedit");
auto textedit = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "textedit");
auto listview = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "listview");
auto dropdown = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "dropdown");
auto scrollbar = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "scrollbar");
auto tabbar = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "tabbar");
auto tabbar_tab = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "tabbar-tab");
auto tabbar_spacer = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "tabbar-spacer");
auto tabwidget_stack = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "tabwidget-stack");
auto checkbox_label = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "checkbox-label");
auto menubar = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "menubar");
auto menubaritem = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "menubaritem");
auto menu = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "menu");
auto menuitem = RegisterStyle(std::make_unique<BasicWidgetStyle>(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
}) {};

View file

@ -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)

View file

@ -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++)
{

View file

@ -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;
}

View file

@ -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();

View file

@ -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();

View file

@ -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<double>& 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<std::string> 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);

View file

@ -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");
}

View file

@ -3,13 +3,16 @@
#include "core/colorf.h"
#include <stdexcept>
#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"));
}

View file

@ -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<Image>& 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)

View file

@ -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);
}

View file

@ -73,8 +73,7 @@ std::vector<std::string> 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;
}

View file

@ -3,6 +3,7 @@
#include "win32_display_window.h"
#include "core/widget.h"
#include <stdexcept>
#include <exception>
#include <thread>
#include <mutex>
#include <condition_variable>
@ -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))
{

View file

@ -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;

View file

@ -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<double>(disp_width_px) / static_cast<double>(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<uint8_t> 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<double> 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)

View file

@ -7,6 +7,7 @@
#include <X11/cursorfont.h>
#include <X11/keysymdef.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XInput2.h>
#include <map>
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;