Merge commit '87689ccb5f' into theme

This commit is contained in:
Marcus Minhorst 2025-08-26 22:09:24 -04:00
commit 27a934ee70
27 changed files with 925 additions and 519 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,14 +253,70 @@ 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)
else()
set(CXX_WARNING_FLAGS -Wall -Wpedantic)
endif()
add_library(zwidget STATIC ${ZWIDGET_SOURCES} ${ZWIDGET_INCLUDES})
target_compile_options(zwidget PRIVATE ${ZWIDGET_COMPILE_OPTIONS})
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)
set_property(TARGET zwidget PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
option(ZWIDGET_BUILD_EXAMPLE "Build the zwidget example application" ON)
if(ZWIDGET_BUILD_EXAMPLE)
if (UNIX AND NOT APPLE)
find_package(SDL2 REQUIRED)
endif()
add_executable(zwidget_example WIN32
example/example.cpp
example/picopng.cpp
example/picopng.h
)
target_compile_options(zwidget_example PRIVATE ${CXX_WARNING_FLAGS})
add_custom_command(
TARGET zwidget_example POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/example/banner.png"
"${CMAKE_CURRENT_BINARY_DIR}/banner.png"
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/example/OpenSans.ttf"
"${CMAKE_CURRENT_BINARY_DIR}/OpenSans.ttf"
)
target_include_directories(zwidget_example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/example)
target_link_libraries(zwidget_example PRIVATE zwidget)
if(WIN32)
target_compile_definitions(zwidget_example PRIVATE UNICODE _UNICODE)
target_link_libraries(zwidget_example PRIVATE gdi32 user32 shell32 comdlg32)
elseif(APPLE)
target_link_libraries(zwidget_example PRIVATE "-framework Cocoa -framework OpenGL")
else()
target_link_libraries(zwidget_example PRIVATE ${ZWIDGET_LIBS})
endif()
set_target_properties(zwidget_example PROPERTIES CXX_STANDARD 20)
if(MSVC)
set_property(TARGET zwidget_example PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
endif()

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;