Update to latest ZWidget version
This adds new features (such as themes) alongside fixing numerous bugs. This should be kept up-to-date with upstream more often and changes to it should be PR'd back to its main repo.
This commit is contained in:
parent
f4eebd1ced
commit
885c1d2920
101 changed files with 10156 additions and 2204 deletions
|
|
@ -1,6 +1,22 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
project(zwidget)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(DBUS dbus-1)
|
||||
if (!DBUS_FOUND)
|
||||
pkg_check_modules(DBUS REQUIRED dbus) # Fedora Linux looks for dbus instead
|
||||
endif()
|
||||
pkg_check_modules(WAYLANDPP
|
||||
wayland-client
|
||||
wayland-client++
|
||||
wayland-client-extra++
|
||||
wayland-client-unstable++
|
||||
wayland-cursor++
|
||||
xkbcommon>=0.5.0
|
||||
)
|
||||
endif()
|
||||
|
||||
set(ZWIDGET_SOURCES
|
||||
src/core/canvas.cpp
|
||||
src/core/font.cpp
|
||||
|
|
@ -8,6 +24,7 @@ set(ZWIDGET_SOURCES
|
|||
src/core/span_layout.cpp
|
||||
src/core/timer.cpp
|
||||
src/core/widget.cpp
|
||||
src/core/theme.cpp
|
||||
src/core/utf8reader.cpp
|
||||
src/core/pathfill.cpp
|
||||
src/core/truetypefont.cpp
|
||||
|
|
@ -32,6 +49,17 @@ set(ZWIDGET_SOURCES
|
|||
src/widgets/listview/listview.cpp
|
||||
src/widgets/tabwidget/tabwidget.cpp
|
||||
src/window/window.cpp
|
||||
src/window/stub/stub_open_folder_dialog.cpp
|
||||
src/window/stub/stub_open_folder_dialog.h
|
||||
src/window/stub/stub_open_file_dialog.cpp
|
||||
src/window/stub/stub_open_file_dialog.h
|
||||
src/window/stub/stub_save_file_dialog.cpp
|
||||
src/window/stub/stub_save_file_dialog.h
|
||||
src/window/ztimer/ztimer.h
|
||||
src/window/ztimer/ztimer.cpp
|
||||
src/systemdialogs/open_folder_dialog.cpp
|
||||
src/systemdialogs/open_file_dialog.cpp
|
||||
src/systemdialogs/save_file_dialog.cpp
|
||||
)
|
||||
|
||||
set(ZWIDGET_INCLUDES
|
||||
|
|
@ -44,6 +72,7 @@ set(ZWIDGET_INCLUDES
|
|||
include/zwidget/core/span_layout.h
|
||||
include/zwidget/core/timer.h
|
||||
include/zwidget/core/widget.h
|
||||
include/zwidget/core/theme.h
|
||||
include/zwidget/core/utf8reader.h
|
||||
include/zwidget/core/resourcedata.h
|
||||
include/zwidget/widgets/lineedit/lineedit.h
|
||||
|
|
@ -61,19 +90,62 @@ set(ZWIDGET_INCLUDES
|
|||
include/zwidget/widgets/listview/listview.h
|
||||
include/zwidget/widgets/tabwidget/tabwidget.h
|
||||
include/zwidget/window/window.h
|
||||
include/zwidget/window/x11nativehandle.h
|
||||
include/zwidget/window/waylandnativehandle.h
|
||||
include/zwidget/window/win32nativehandle.h
|
||||
include/zwidget/window/sdl2nativehandle.h
|
||||
include/zwidget/systemdialogs/open_folder_dialog.h
|
||||
include/zwidget/systemdialogs/open_file_dialog.h
|
||||
include/zwidget/systemdialogs/save_file_dialog.h
|
||||
)
|
||||
|
||||
set(ZWIDGET_WIN32_SOURCES
|
||||
src/window/win32/win32window.cpp
|
||||
src/window/win32/win32window.h
|
||||
src/window/win32/win32_display_backend.cpp
|
||||
src/window/win32/win32_display_backend.h
|
||||
src/window/win32/win32_display_window.cpp
|
||||
src/window/win32/win32_display_window.h
|
||||
src/window/win32/win32_open_folder_dialog.cpp
|
||||
src/window/win32/win32_open_folder_dialog.h
|
||||
src/window/win32/win32_open_file_dialog.cpp
|
||||
src/window/win32/win32_open_file_dialog.h
|
||||
src/window/win32/win32_save_file_dialog.cpp
|
||||
src/window/win32/win32_save_file_dialog.h
|
||||
src/window/win32/win32_util.h
|
||||
)
|
||||
|
||||
set(ZWIDGET_DBUS_SOURCES
|
||||
src/window/dbus/dbus_open_folder_dialog.cpp
|
||||
src/window/dbus/dbus_open_folder_dialog.h
|
||||
src/window/dbus/dbus_open_file_dialog.cpp
|
||||
src/window/dbus/dbus_open_file_dialog.h
|
||||
src/window/dbus/dbus_save_file_dialog.cpp
|
||||
src/window/dbus/dbus_save_file_dialog.h
|
||||
)
|
||||
|
||||
set(ZWIDGET_COCOA_SOURCES
|
||||
)
|
||||
|
||||
set(ZWIDGET_SDL2_SOURCES
|
||||
src/window/sdl2/sdl2displaywindow.cpp
|
||||
src/window/sdl2/sdl2displaywindow.h
|
||||
src/window/sdl2/sdl2_display_backend.cpp
|
||||
src/window/sdl2/sdl2_display_backend.h
|
||||
src/window/sdl2/sdl2_display_window.cpp
|
||||
src/window/sdl2/sdl2_display_window.h
|
||||
)
|
||||
|
||||
set(ZWIDGET_X11_SOURCES
|
||||
src/window/x11/x11_display_backend.cpp
|
||||
src/window/x11/x11_display_backend.h
|
||||
src/window/x11/x11_display_window.cpp
|
||||
src/window/x11/x11_display_window.h
|
||||
)
|
||||
|
||||
set(ZWIDGET_WAYLAND_SOURCES
|
||||
src/window/wayland/wayland_display_backend.cpp
|
||||
src/window/wayland/wayland_display_backend.h
|
||||
src/window/wayland/wayland_display_window.cpp
|
||||
src/window/wayland/wayland_display_window.h
|
||||
src/window/wayland/wl_fractional_scaling_protocol.cpp
|
||||
src/window/wayland/wl_fractional_scaling_protocol.hpp
|
||||
)
|
||||
|
||||
source_group("src" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/.+")
|
||||
|
|
@ -95,6 +167,13 @@ source_group("src\\widgets\\checkboxlabel" REGULAR_EXPRESSION "${CMAKE_CURRENT_S
|
|||
source_group("src\\widgets\\listview" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/widgets/listview/.+")
|
||||
source_group("src\\widgets\\tabwidget" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/widgets/tabwidget/.+")
|
||||
source_group("src\\window" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/window/.+")
|
||||
source_group("src\\window\\stub" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/window/stub/.+")
|
||||
source_group("src\\window\\win32" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/window/win32/.+")
|
||||
source_group("src\\window\\sdl2" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/window/sdl2/.+")
|
||||
source_group("src\\window\\x11" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/window/x11/.+")
|
||||
source_group("src\\window\\wayland" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/window/wayland/.+")
|
||||
source_group("src\\window\\dbus" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/window/dbus/.+")
|
||||
source_group("src\\systemdialogs" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/src/systemdialogs/.+")
|
||||
source_group("include" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/.+")
|
||||
source_group("include\\core" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/core/.+")
|
||||
source_group("include\\widgets" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/widgets/.+")
|
||||
|
|
@ -112,42 +191,62 @@ source_group("include\\widgets\\checkboxlabel" REGULAR_EXPRESSION "${CMAKE_CURRE
|
|||
source_group("include\\widgets\\listview" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/widgets/listview/.+")
|
||||
source_group("include\\widgets\\tabwidget" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/widgets/tabwidget/.+")
|
||||
source_group("include\\window" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/window/.+")
|
||||
source_group("include\\window\\win32" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/window/win32/.+")
|
||||
source_group("include\\window\\sdl2" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/window/sdl2/.+")
|
||||
source_group("include\\systemdialogs" REGULAR_EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/include/zwidget/systemdialogs/.+")
|
||||
|
||||
include_directories(include include/zwidget src)
|
||||
# Include directory to external projects using zwidget
|
||||
include_directories(include)
|
||||
|
||||
# Internal include dirs for building zwidget
|
||||
set(ZWIDGET_INCLUDE_DIRS include/zwidget src)
|
||||
|
||||
set(ZWIDGET_COMPILE_OPTIONS)
|
||||
|
||||
if(WIN32)
|
||||
set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_WIN32_SOURCES})
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
if(MINGW)
|
||||
add_definitions(-DMINGW)
|
||||
set(ZWIDGET_DEFINES -DUNICODE -D_UNICODE)
|
||||
set(ZWIDGET_LINK_OPTIONS)
|
||||
if(MSVC)
|
||||
# Use all cores for compilation
|
||||
set(ZWIDGET_COMPILE_OPTIONS ${ZWIDGET_COMPILE_OPTIONS} /MP)
|
||||
|
||||
# Ignore specific warnings
|
||||
#set(ZWIDGET_COMPILE_OPTIONS ${ZWIDGET_COMPILE_OPTIONS} /wd4244 /wd4267 /wd4005 /wd4018)
|
||||
|
||||
# Don't slow down std containers in debug builds
|
||||
set(ZWIDGET_DEFINES ${ZWIDGET_DEFINES})
|
||||
|
||||
# Ignore warning about legacy CRT functions
|
||||
#set(ZWIDGET_DEFINES ${ZWIDGET_DEFINES} -D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_COCOA_SOURCES})
|
||||
set(ZWIDGET_LIBS ${CMAKE_DL_LIBS} -ldl)
|
||||
add_definitions(-DUNIX -D_UNIX)
|
||||
add_link_options(-pthread)
|
||||
set(ZWIDGET_DEFINES -DUNIX -D_UNIX)
|
||||
set(ZWIDGET_LINK_OPTIONS -pthread)
|
||||
else()
|
||||
set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_SDL2_SOURCES})
|
||||
if(NOT HAIKU)
|
||||
set(ZWIDGET_LIBS ${CMAKE_DL_LIBS} -ldl)
|
||||
else()
|
||||
set(ZWIDGET_LIBS ${CMAKE_DL_LIBS})
|
||||
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_LINK_OPTIONS -pthread)
|
||||
if (DBUS_FOUND)
|
||||
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})
|
||||
set(ZWIDGET_DEFINES ${ZWIDGET_DEFINES} -DUSE_DBUS)
|
||||
endif()
|
||||
if (WAYLANDPP_FOUND)
|
||||
set(ZWIDGET_SOURCES ${ZWIDGET_SOURCES} ${ZWIDGET_WAYLAND_SOURCES})
|
||||
set(ZWIDGET_INCLUDE_DIRS ${ZWIDGET_INCLUDE_DIRS} ${WAYLANDPP_INCLUDE_DIRS})
|
||||
set(ZWIDGET_LIBS ${ZWIDGET_LIBS} ${WAYLANDPP_LDFLAGS})
|
||||
set(ZWIDGET_DEFINES ${ZWIDGET_DEFINES} -DUSE_WAYLAND)
|
||||
endif()
|
||||
add_definitions(-DUNIX -D_UNIX)
|
||||
add_link_options(-pthread)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# Use all cores for compilation
|
||||
set(CMAKE_CXX_FLAGS "/MP ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
# Ignore warnings in third party code
|
||||
#set_source_files_properties(${ZWIDGET_SOURCES} PROPERTIES COMPILE_FLAGS "/wd4244 /wd4267 /wd4005 /wd4018 -D_CRT_SECURE_NO_WARNINGS")
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,26 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "image.h"
|
||||
#include "rect.h"
|
||||
#include <vector>
|
||||
|
||||
class Font;
|
||||
class Image;
|
||||
class Point;
|
||||
class Rect;
|
||||
class Colorf;
|
||||
class DisplayWindow;
|
||||
struct VerticalTextPosition;
|
||||
class CanvasFontGroup;
|
||||
|
||||
class CanvasTexture
|
||||
{
|
||||
public:
|
||||
virtual ~CanvasTexture() = default;
|
||||
|
||||
int Width = 0;
|
||||
int Height = 0;
|
||||
};
|
||||
|
||||
class FontMetrics
|
||||
{
|
||||
|
|
@ -20,44 +32,85 @@ public:
|
|||
double height = 0.0;
|
||||
};
|
||||
|
||||
class Canvas
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<Canvas> create(DisplayWindow* window);
|
||||
|
||||
virtual ~Canvas() = default;
|
||||
|
||||
virtual void begin(const Colorf& color) = 0;
|
||||
virtual void end() = 0;
|
||||
|
||||
virtual void begin3d() = 0;
|
||||
virtual void end3d() = 0;
|
||||
|
||||
virtual Point getOrigin() = 0;
|
||||
virtual void setOrigin(const Point& origin) = 0;
|
||||
|
||||
virtual void pushClip(const Rect& box) = 0;
|
||||
virtual void popClip() = 0;
|
||||
|
||||
virtual void fillRect(const Rect& box, const Colorf& color) = 0;
|
||||
virtual void line(const Point& p0, const Point& p1, const Colorf& color) = 0;
|
||||
|
||||
virtual void drawText(const Point& pos, const Colorf& color, const std::string& text) = 0;
|
||||
virtual Rect measureText(const std::string& text) = 0;
|
||||
virtual VerticalTextPosition verticalTextAlign() = 0;
|
||||
|
||||
virtual void drawText(const std::shared_ptr<Font>& font, const Point& pos, const std::string& text, const Colorf& color) = 0;
|
||||
virtual void drawTextEllipsis(const std::shared_ptr<Font>& font, const Point& pos, const Rect& clipBox, const std::string& text, const Colorf& color) = 0;
|
||||
virtual Rect measureText(const std::shared_ptr<Font>& font, const std::string& text) = 0;
|
||||
virtual FontMetrics getFontMetrics(const std::shared_ptr<Font>& font) = 0;
|
||||
virtual int getCharacterIndex(const std::shared_ptr<Font>& font, const std::string& text, const Point& hitPoint) = 0;
|
||||
|
||||
virtual void drawImage(const std::shared_ptr<Image>& image, const Point& pos) = 0;
|
||||
};
|
||||
|
||||
struct VerticalTextPosition
|
||||
{
|
||||
double top = 0.0;
|
||||
double baseline = 0.0;
|
||||
double bottom = 0.0;
|
||||
};
|
||||
|
||||
class Canvas
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<Canvas> create();
|
||||
|
||||
Canvas();
|
||||
virtual ~Canvas();
|
||||
|
||||
virtual void attach(DisplayWindow* window);
|
||||
virtual void detach();
|
||||
|
||||
virtual void begin(const Colorf& color);
|
||||
virtual void end() { }
|
||||
|
||||
virtual void begin3d() { }
|
||||
virtual void end3d() { }
|
||||
|
||||
Point getOrigin();
|
||||
void setOrigin(const Point& origin);
|
||||
|
||||
void pushClip(const Rect& box);
|
||||
void popClip();
|
||||
|
||||
void fillRect(const Rect& box, const Colorf& color);
|
||||
void line(const Point& p0, const Point& p1, const Colorf& color);
|
||||
|
||||
void drawText(const Point& pos, const Colorf& color, const std::string& text);
|
||||
Rect measureText(const std::string& text);
|
||||
VerticalTextPosition verticalTextAlign();
|
||||
|
||||
void drawText(const std::shared_ptr<Font>& font, const Point& pos, const std::string& text, const Colorf& color);
|
||||
void drawTextEllipsis(const std::shared_ptr<Font>& font, const Point& pos, const Rect& clipBox, const std::string& text, const Colorf& color);
|
||||
Rect measureText(const std::shared_ptr<Font>& font, const std::string& text);
|
||||
FontMetrics getFontMetrics(const std::shared_ptr<Font>& font);
|
||||
int getCharacterIndex(const std::shared_ptr<Font>& font, const std::string& text, const Point& hitPoint);
|
||||
|
||||
void drawImage(const std::shared_ptr<Image>& image, const Point& pos);
|
||||
void drawImage(const std::shared_ptr<Image>& image, const Rect& box);
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<CanvasTexture> createTexture(int width, int height, const void* pixels, ImageFormat format = ImageFormat::B8G8R8A8) = 0;
|
||||
virtual void drawLineAntialiased(float x0, float y0, float x1, float y1, Colorf color) = 0;
|
||||
virtual void fillTile(float x, float y, float width, float height, Colorf color) = 0;
|
||||
virtual void drawTile(CanvasTexture* texture, float x, float y, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color) = 0;
|
||||
virtual void drawGlyph(CanvasTexture* texture, float x, float y, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color) = 0;
|
||||
|
||||
int getClipMinX() const;
|
||||
int getClipMinY() const;
|
||||
int getClipMaxX() const;
|
||||
int getClipMaxY() const;
|
||||
|
||||
template<typename T>
|
||||
static T clamp(T val, T minval, T maxval) { return std::max<T>(std::min<T>(val, maxval), minval); }
|
||||
|
||||
DisplayWindow* window = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
double uiscale = 1.0f;
|
||||
|
||||
std::unique_ptr<CanvasTexture> whiteTexture;
|
||||
|
||||
private:
|
||||
void setLanguage(const char* lang) { language = lang; }
|
||||
void drawLineUnclipped(const Point& p0, const Point& p1, const Colorf& color);
|
||||
|
||||
std::unique_ptr<CanvasFontGroup> font;
|
||||
|
||||
Point origin;
|
||||
std::vector<Rect> clipStack;
|
||||
|
||||
std::unordered_map<std::shared_ptr<Image>, std::unique_ptr<CanvasTexture>> imageTextures;
|
||||
std::string language;
|
||||
|
||||
friend class CanvasFont;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
class Point
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
81
libraries/ZWidget/include/zwidget/core/theme.h
Normal file
81
libraries/ZWidget/include/zwidget/core/theme.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
#include "rect.h"
|
||||
#include "colorf.h"
|
||||
|
||||
class Widget;
|
||||
class Canvas;
|
||||
|
||||
class WidgetStyle
|
||||
{
|
||||
public:
|
||||
WidgetStyle(WidgetStyle* parentStyle = nullptr) : ParentStyle(parentStyle) { }
|
||||
virtual ~WidgetStyle() = default;
|
||||
virtual void Paint(Widget* widget, Canvas* canvas, Size size) = 0;
|
||||
|
||||
void SetBool(const std::string& state, const std::string& propertyName, bool value);
|
||||
void SetInt(const std::string& state, const std::string& propertyName, int value);
|
||||
void SetDouble(const std::string& state, const std::string& propertyName, double value);
|
||||
void SetString(const std::string& state, const std::string& propertyName, const std::string& value);
|
||||
void SetColor(const std::string& state, const std::string& propertyName, const Colorf& value);
|
||||
|
||||
void SetBool(const std::string& propertyName, bool value) { SetBool(std::string(), propertyName, value); }
|
||||
void SetInt(const std::string& propertyName, int value) { SetInt(std::string(), propertyName, value); }
|
||||
void SetDouble(const std::string& propertyName, double value) { SetDouble(std::string(), propertyName, value); }
|
||||
void SetString(const std::string& propertyName, const std::string& value) { SetString(std::string(), propertyName, value); }
|
||||
void SetColor(const std::string& propertyName, const Colorf& value) { SetColor(std::string(), propertyName, value); }
|
||||
|
||||
private:
|
||||
// Note: do not call these directly. Use widget->GetStyleXX instead since a widget may explicitly override a class style
|
||||
bool GetBool(const std::string& state, const std::string& propertyName) const;
|
||||
int GetInt(const std::string& state, const std::string& propertyName) const;
|
||||
double GetDouble(const std::string& state, const std::string& propertyName) const;
|
||||
std::string GetString(const std::string& state, const std::string& propertyName) const;
|
||||
Colorf GetColor(const std::string& state, const std::string& propertyName) const;
|
||||
|
||||
WidgetStyle* ParentStyle = nullptr;
|
||||
typedef std::variant<bool, int, double, std::string, Colorf> PropertyVariant;
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, PropertyVariant>> StyleProperties;
|
||||
|
||||
const PropertyVariant* FindProperty(const std::string& state, const std::string& propertyName) const;
|
||||
|
||||
friend class Widget;
|
||||
};
|
||||
|
||||
class BasicWidgetStyle : public WidgetStyle
|
||||
{
|
||||
public:
|
||||
using WidgetStyle::WidgetStyle;
|
||||
void Paint(Widget* widget, Canvas* canvas, Size size) override;
|
||||
};
|
||||
|
||||
class WidgetTheme
|
||||
{
|
||||
public:
|
||||
virtual ~WidgetTheme() = default;
|
||||
|
||||
WidgetStyle* RegisterStyle(std::unique_ptr<WidgetStyle> widgetStyle, const std::string& widgetClass);
|
||||
WidgetStyle* GetStyle(const std::string& widgetClass);
|
||||
|
||||
static void SetTheme(std::unique_ptr<WidgetTheme> theme);
|
||||
static WidgetTheme* GetTheme();
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::unique_ptr<WidgetStyle>> Styles;
|
||||
};
|
||||
|
||||
class DarkWidgetTheme : public WidgetTheme
|
||||
{
|
||||
public:
|
||||
DarkWidgetTheme();
|
||||
};
|
||||
|
||||
class LightWidgetTheme : public WidgetTheme
|
||||
{
|
||||
public:
|
||||
LightWidgetTheme();
|
||||
};
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
#include "canvas.h"
|
||||
#include "rect.h"
|
||||
#include "colorf.h"
|
||||
|
|
@ -20,7 +22,7 @@ enum class WidgetType
|
|||
class Widget : DisplayWindowHost
|
||||
{
|
||||
public:
|
||||
Widget(Widget* parent = nullptr, WidgetType type = WidgetType::Child);
|
||||
Widget(Widget* parent = nullptr, WidgetType type = WidgetType::Child, RenderAPI api = RenderAPI::Unspecified);
|
||||
virtual ~Widget();
|
||||
|
||||
void SetParent(Widget* parent);
|
||||
|
|
@ -39,16 +41,45 @@ public:
|
|||
|
||||
// Widget noncontent area
|
||||
void SetNoncontentSizes(double left, double top, double right, double bottom);
|
||||
double GetNoncontentLeft() const { return Noncontent.Left; }
|
||||
double GetNoncontentTop() const { return Noncontent.Top; }
|
||||
double GetNoncontentRight() const { return Noncontent.Right; }
|
||||
double GetNoncontentBottom() const { return Noncontent.Bottom; }
|
||||
double GetNoncontentLeft() const { return GridFitSize(GetStyleDouble("noncontent-left")); }
|
||||
double GetNoncontentTop() const { return GridFitSize(GetStyleDouble("noncontent-top")); }
|
||||
double GetNoncontentRight() const { return GridFitSize(GetStyleDouble("noncontent-right")); }
|
||||
double GetNoncontentBottom() const { return GridFitSize(GetStyleDouble("noncontent-bottom")); }
|
||||
|
||||
// Get the DPI scale factor for the window the widget is located on
|
||||
double GetDpiScale() const;
|
||||
|
||||
// Align point to the nearest physical screen pixel
|
||||
double GridFitPoint(double p) const;
|
||||
Point GridFitPoint(double x, double y) const { return GridFitPoint(Point(x, y)); }
|
||||
Point GridFitPoint(Point p) const { return Point(GridFitPoint(p.x), GridFitPoint(p.y)); }
|
||||
|
||||
// Convert size to exactly covering physical screen pixels
|
||||
double GridFitSize(double s) const;
|
||||
Size GridFitSize(double w, double h) const { return GridFitSize(Size(w, h)); }
|
||||
Size GridFitSize(Size s) const { return Size(GridFitSize(s.width), GridFitSize(s.height)); }
|
||||
|
||||
// Widget frame box
|
||||
Rect GetFrameGeometry() const;
|
||||
void SetFrameGeometry(const Rect& geometry);
|
||||
void SetFrameGeometry(double x, double y, double width, double height) { SetFrameGeometry(Rect::xywh(x, y, width, height)); }
|
||||
|
||||
// Style properties
|
||||
void SetStyleClass(const std::string& styleClass);
|
||||
const std::string& GetStyleClass() const { return StyleClass; }
|
||||
void SetStyleState(const std::string& state);
|
||||
const std::string& GetStyleState() const { return StyleState; }
|
||||
void SetStyleBool(const std::string& propertyName, bool value);
|
||||
void SetStyleInt(const std::string& propertyName, int value);
|
||||
void SetStyleDouble(const std::string& propertyName, double value);
|
||||
void SetStyleString(const std::string& propertyName, const std::string& value);
|
||||
void SetStyleColor(const std::string& propertyName, const Colorf& value);
|
||||
bool GetStyleBool(const std::string& propertyName) const;
|
||||
int GetStyleInt(const std::string& propertyName) const;
|
||||
double GetStyleDouble(const std::string& propertyName) const;
|
||||
std::string GetStyleString(const std::string& propertyName) const;
|
||||
Colorf GetStyleColor(const std::string& propertyName) const;
|
||||
|
||||
void SetWindowBackground(const Colorf& color);
|
||||
void SetWindowBorderColor(const Colorf& color);
|
||||
void SetWindowCaptionColor(const Colorf& color);
|
||||
|
|
@ -73,6 +104,7 @@ public:
|
|||
bool IsEnabled();
|
||||
bool IsVisible();
|
||||
bool IsHidden();
|
||||
bool IsFullscreen();
|
||||
|
||||
void SetFocus();
|
||||
void SetEnabled(bool value);
|
||||
|
|
@ -82,19 +114,26 @@ public:
|
|||
void LockCursor();
|
||||
void UnlockCursor();
|
||||
void SetCursor(StandardCursor cursor);
|
||||
void CaptureMouse();
|
||||
void ReleaseMouseCapture();
|
||||
|
||||
bool GetKeyState(EInputKey key);
|
||||
void SetPointerCapture();
|
||||
void ReleasePointerCapture();
|
||||
|
||||
void SetModalCapture();
|
||||
void ReleaseModalCapture();
|
||||
|
||||
bool GetKeyState(InputKey key);
|
||||
|
||||
std::string GetClipboardText();
|
||||
void SetClipboardText(const std::string& text);
|
||||
|
||||
Widget* Window();
|
||||
Widget* Window() const;
|
||||
Canvas* GetCanvas() const;
|
||||
Widget* ChildAt(double x, double y) { return ChildAt(Point(x, y)); }
|
||||
Widget* ChildAt(const Point& pos);
|
||||
|
||||
bool IsParent(const Widget* w) const;
|
||||
bool IsChild(const Widget* w) const;
|
||||
|
||||
Widget* Parent() const { return ParentObj; }
|
||||
Widget* PrevSibling() const { return PrevSiblingObj; }
|
||||
Widget* NextSibling() const { return NextSiblingObj; }
|
||||
|
|
@ -111,19 +150,28 @@ public:
|
|||
|
||||
static Size GetScreenSize();
|
||||
|
||||
void SetCanvas(std::unique_ptr<Canvas> canvas);
|
||||
void* GetNativeHandle();
|
||||
int GetNativePixelWidth();
|
||||
int GetNativePixelHeight();
|
||||
|
||||
// Vulkan support:
|
||||
std::vector<std::string> GetVulkanInstanceExtensions() { return Window()->DispWindow->GetVulkanInstanceExtensions(); }
|
||||
VkSurfaceKHR CreateVulkanSurface(VkInstance instance) { return Window()->DispWindow->CreateVulkanSurface(instance); }
|
||||
|
||||
protected:
|
||||
virtual void OnPaintFrame(Canvas* canvas) { }
|
||||
virtual void OnPaintFrame(Canvas* canvas);
|
||||
virtual void OnPaint(Canvas* canvas) { }
|
||||
virtual bool OnMouseDown(const Point& pos, int key) { return false; }
|
||||
virtual bool OnMouseDoubleclick(const Point& pos, int key) { return false; }
|
||||
virtual bool OnMouseUp(const Point& pos, int key) { return false; }
|
||||
virtual bool OnMouseWheel(const Point& pos, EInputKey key) { return false; }
|
||||
virtual bool OnMouseDown(const Point& pos, InputKey key) { return false; }
|
||||
virtual bool OnMouseDoubleclick(const Point& pos, InputKey key) { return false; }
|
||||
virtual bool OnMouseUp(const Point& pos, InputKey key) { return false; }
|
||||
virtual bool OnMouseWheel(const Point& pos, InputKey key) { return false; }
|
||||
virtual void OnMouseMove(const Point& pos) { }
|
||||
virtual void OnMouseLeave() { }
|
||||
virtual void OnRawMouseMove(int dx, int dy) { }
|
||||
virtual void OnKeyChar(std::string chars) { }
|
||||
virtual void OnKeyDown(EInputKey key) { }
|
||||
virtual void OnKeyUp(EInputKey key) { }
|
||||
virtual void OnKeyDown(InputKey key) { }
|
||||
virtual void OnKeyUp(InputKey key) { }
|
||||
virtual void OnGeometryChanged() { }
|
||||
virtual void OnClose() { delete this; }
|
||||
virtual void OnSetFocus() { }
|
||||
|
|
@ -138,14 +186,15 @@ private:
|
|||
// DisplayWindowHost
|
||||
void OnWindowPaint() override;
|
||||
void OnWindowMouseMove(const Point& pos) override;
|
||||
void OnWindowMouseDown(const Point& pos, EInputKey key) override;
|
||||
void OnWindowMouseDoubleclick(const Point& pos, EInputKey key) override;
|
||||
void OnWindowMouseUp(const Point& pos, EInputKey key) override;
|
||||
void OnWindowMouseWheel(const Point& pos, EInputKey key) override;
|
||||
void OnWindowMouseLeave() override;
|
||||
void OnWindowMouseDown(const Point& pos, InputKey key) override;
|
||||
void OnWindowMouseDoubleclick(const Point& pos, InputKey key) override;
|
||||
void OnWindowMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnWindowMouseWheel(const Point& pos, InputKey key) override;
|
||||
void OnWindowRawMouseMove(int dx, int dy) override;
|
||||
void OnWindowKeyChar(std::string chars) override;
|
||||
void OnWindowKeyDown(EInputKey key) override;
|
||||
void OnWindowKeyUp(EInputKey key) override;
|
||||
void OnWindowKeyDown(InputKey key) override;
|
||||
void OnWindowKeyUp(InputKey key) override;
|
||||
void OnWindowGeometryChanged() override;
|
||||
void OnWindowClose() override;
|
||||
void OnWindowActivated() override;
|
||||
|
|
@ -167,14 +216,6 @@ private:
|
|||
|
||||
Colorf WindowBackground = Colorf::fromRgba8(240, 240, 240);
|
||||
|
||||
struct
|
||||
{
|
||||
double Left = 0.0;
|
||||
double Top = 0.0;
|
||||
double Right = 0.0;
|
||||
double Bottom = 0.0;
|
||||
} Noncontent;
|
||||
|
||||
std::string WindowTitle;
|
||||
std::unique_ptr<DisplayWindow> DispWindow;
|
||||
std::unique_ptr<Canvas> DispCanvas;
|
||||
|
|
@ -185,8 +226,16 @@ private:
|
|||
|
||||
StandardCursor CurrentCursor = StandardCursor::arrow;
|
||||
|
||||
std::string StyleClass = "widget";
|
||||
std::string StyleState;
|
||||
typedef std::variant<bool, int, double, std::string, Colorf> PropertyVariant;
|
||||
std::unordered_map<std::string, PropertyVariant> StyleProperties;
|
||||
|
||||
Widget(const Widget&) = delete;
|
||||
Widget& operator=(const Widget&) = delete;
|
||||
|
||||
friend class Timer;
|
||||
friend class OpenFileDialog;
|
||||
friend class OpenFolderDialog;
|
||||
friend class SaveFileDialog;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Widget;
|
||||
|
||||
/// \brief Displays the system open file dialog.
|
||||
class OpenFileDialog
|
||||
{
|
||||
public:
|
||||
/// \brief Constructs an open file dialog.
|
||||
static std::unique_ptr<OpenFileDialog> Create(Widget* owner);
|
||||
|
||||
virtual ~OpenFileDialog() = default;
|
||||
|
||||
/// \brief Get the full path of the file selected.
|
||||
///
|
||||
/// If multiple files are selected, this returns the first file.
|
||||
virtual std::string Filename() const = 0;
|
||||
|
||||
/// \brief Gets an array that contains one file name for each selected file.
|
||||
virtual std::vector<std::string> Filenames() const = 0;
|
||||
|
||||
/// \brief Sets if multiple files can be selected or not.
|
||||
/// \param multiselect = When true, multiple items can be selected.
|
||||
virtual void SetMultiSelect(bool multiselect) = 0;
|
||||
|
||||
/// \brief Sets a string containing the full path of the file selected.
|
||||
virtual void SetFilename(const std::string &filename) = 0;
|
||||
|
||||
/// \brief Sets the default extension to use.
|
||||
virtual void SetDefaultExtension(const std::string& extension) = 0;
|
||||
|
||||
/// \brief Add a filter that determines what types of files are displayed.
|
||||
virtual void AddFilter(const std::string &filter_description, const std::string &filter_extension) = 0;
|
||||
|
||||
/// \brief Clears all filters.
|
||||
virtual void ClearFilters() = 0;
|
||||
|
||||
/// \brief Sets a default filter, on a 0-based index.
|
||||
virtual void SetFilterIndex(int filter_index) = 0;
|
||||
|
||||
/// \brief Sets the initial directory that is displayed.
|
||||
virtual void SetInitialDirectory(const std::string &path) = 0;
|
||||
|
||||
/// \brief Sets the text that appears in the title bar.
|
||||
virtual void SetTitle(const std::string &title) = 0;
|
||||
|
||||
/// \brief Shows the file dialog.
|
||||
/// \return true if the user clicks the OK button of the dialog that is displayed, false otherwise.
|
||||
virtual bool Show() = 0;
|
||||
};
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Widget;
|
||||
|
||||
/// \brief Displays the system folder browsing dialog
|
||||
class OpenFolderDialog
|
||||
{
|
||||
public:
|
||||
/// \brief Constructs an browse folder dialog.
|
||||
static std::unique_ptr<OpenFolderDialog> Create(Widget*owner);
|
||||
|
||||
virtual ~OpenFolderDialog() = default;
|
||||
|
||||
/// \brief Get the full path of the directory selected.
|
||||
virtual std::string SelectedPath() const = 0;
|
||||
|
||||
/// \brief Sets the initial directory that is displayed.
|
||||
virtual void SetInitialDirectory(const std::string& path) = 0;
|
||||
|
||||
/// \brief Sets the text that appears in the title bar.
|
||||
virtual void SetTitle(const std::string& title) = 0;
|
||||
|
||||
/// \brief Shows the file dialog.
|
||||
/// \return true if the user clicks the OK button of the dialog that is displayed, false otherwise.
|
||||
virtual bool Show() = 0;
|
||||
};
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Widget;
|
||||
|
||||
/// \brief Displays the system save file dialog.
|
||||
class SaveFileDialog
|
||||
{
|
||||
public:
|
||||
/// \brief Constructs a save file dialog.
|
||||
static std::unique_ptr<SaveFileDialog> Create(Widget *owner);
|
||||
|
||||
virtual ~SaveFileDialog() = default;
|
||||
|
||||
/// \brief Get the full path of the file selected.
|
||||
virtual std::string Filename() const = 0;
|
||||
|
||||
/// \brief Sets a string containing the full path of the file selected.
|
||||
virtual void SetFilename(const std::string &filename) = 0;
|
||||
|
||||
/// \brief Sets the default extension to use.
|
||||
virtual void SetDefaultExtension(const std::string& extension) = 0;
|
||||
|
||||
/// \brief Add a filter that determines what types of files are displayed.
|
||||
virtual void AddFilter(const std::string &filter_description, const std::string &filter_extension) = 0;
|
||||
|
||||
/// \brief Clears all filters.
|
||||
virtual void ClearFilters() = 0;
|
||||
|
||||
/// \brief Sets a default filter, on a 0-based index.
|
||||
virtual void SetFilterIndex(int filter_index) = 0;
|
||||
|
||||
/// \brief Sets the initial directory that is displayed.
|
||||
virtual void SetInitialDirectory(const std::string &path) = 0;
|
||||
|
||||
/// \brief Sets the text that appears in the title bar.
|
||||
virtual void SetTitle(const std::string &title) = 0;
|
||||
|
||||
/// \brief Shows the file dialog.
|
||||
/// \return true if the user clicks the OK button of the dialog that is displayed, false otherwise.
|
||||
virtual bool Show() = 0;
|
||||
};
|
||||
|
|
@ -21,15 +21,14 @@ public:
|
|||
|
||||
protected:
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
bool OnMouseDown(const Point& pos, int key) override;
|
||||
bool OnMouseUp(const Point& pos, int key) override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnMouseLeave() override;
|
||||
void OnKeyUp(EInputKey key) override;
|
||||
void OnKeyUp(InputKey key) override;
|
||||
|
||||
private:
|
||||
std::string text;
|
||||
bool checked = false;
|
||||
bool radiostyle = false;
|
||||
bool mouseDownActive = false;
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,13 +4,22 @@
|
|||
#include "../../core/widget.h"
|
||||
#include "../../core/image.h"
|
||||
|
||||
enum ImageBoxMode
|
||||
{
|
||||
Center,
|
||||
Contain,
|
||||
Cover
|
||||
};
|
||||
|
||||
class ImageBox : public Widget
|
||||
{
|
||||
public:
|
||||
ImageBox(Widget* parent);
|
||||
|
||||
void SetImage(std::shared_ptr<Image> newImage);
|
||||
void SetImageMode(ImageBoxMode mode);
|
||||
|
||||
double GetPreferredWidth() const;
|
||||
double GetPreferredHeight() const;
|
||||
|
||||
protected:
|
||||
|
|
@ -18,4 +27,5 @@ protected:
|
|||
|
||||
private:
|
||||
std::shared_ptr<Image> image;
|
||||
ImageBoxMode mode = ImageBoxMode::Center;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public:
|
|||
void SetInputMask(const std::string& mask);
|
||||
void SetDecimalCharacter(const std::string& decimal_char);
|
||||
|
||||
std::function<bool(int key)> FuncIgnoreKeyDown;
|
||||
std::function<bool(InputKey key)> FuncIgnoreKeyDown;
|
||||
std::function<std::string(std::string text)> FuncFilterKeyChar;
|
||||
std::function<void()> FuncBeforeEditChanged;
|
||||
std::function<void()> FuncAfterEditChanged;
|
||||
|
|
@ -69,15 +69,14 @@ public:
|
|||
std::function<void()> FuncEnterPressed;
|
||||
|
||||
protected:
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
bool OnMouseDown(const Point& pos, int key) override;
|
||||
bool OnMouseDoubleclick(const Point& pos, int key) override;
|
||||
bool OnMouseUp(const Point& pos, int key) override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseDoubleclick(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnKeyChar(std::string chars) override;
|
||||
void OnKeyDown(EInputKey key) override;
|
||||
void OnKeyUp(EInputKey key) override;
|
||||
void OnKeyDown(InputKey key) override;
|
||||
void OnKeyUp(InputKey key) override;
|
||||
void OnGeometryChanged() override;
|
||||
void OnEnableChanged() override;
|
||||
void OnSetFocus() override;
|
||||
|
|
|
|||
|
|
@ -29,11 +29,10 @@ public:
|
|||
|
||||
protected:
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
bool OnMouseDown(const Point& pos, int key) override;
|
||||
bool OnMouseDoubleclick(const Point& pos, int key) override;
|
||||
bool OnMouseWheel(const Point& pos, EInputKey key) override;
|
||||
void OnKeyDown(EInputKey key) override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseDoubleclick(const Point& pos, InputKey key) override;
|
||||
bool OnMouseWheel(const Point& pos, InputKey key) override;
|
||||
void OnKeyDown(InputKey key) override;
|
||||
void OnGeometryChanged() override;
|
||||
void OnScrollbarScroll();
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ class Statusbar;
|
|||
class MainWindow : public Widget
|
||||
{
|
||||
public:
|
||||
MainWindow();
|
||||
MainWindow(RenderAPI api = RenderAPI::Unspecified);
|
||||
~MainWindow();
|
||||
|
||||
Menubar* GetMenubar() const { return MenubarWidget; }
|
||||
Toolbar* GetToolbar() const { return ToolbarWidget; }
|
||||
Toolbar* GetTopToolbar() const { return TopToolbarWidget; }
|
||||
Toolbar* GetLeftToolbar() const { return LeftToolbarWidget; }
|
||||
Statusbar* GetStatusbar() const { return StatusbarWidget; }
|
||||
Widget* GetCentralWidget() const { return CentralWidget; }
|
||||
|
||||
|
|
@ -25,7 +26,8 @@ protected:
|
|||
|
||||
private:
|
||||
Menubar* MenubarWidget = nullptr;
|
||||
Toolbar* ToolbarWidget = nullptr;
|
||||
Toolbar* TopToolbarWidget = nullptr;
|
||||
Toolbar* LeftToolbarWidget = nullptr;
|
||||
Widget* CentralWidget = nullptr;
|
||||
Statusbar* StatusbarWidget = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../core/widget.h"
|
||||
#include "../textlabel/textlabel.h"
|
||||
#include "../imagebox/imagebox.h"
|
||||
#include <vector>
|
||||
|
||||
class Menu;
|
||||
class MenubarItem;
|
||||
class MenuItem;
|
||||
class MenuItemSeparator;
|
||||
|
||||
class Menubar : public Widget
|
||||
{
|
||||
|
|
@ -9,6 +17,105 @@ public:
|
|||
Menubar(Widget* parent);
|
||||
~Menubar();
|
||||
|
||||
MenubarItem* AddItem(std::string text, std::function<void(Menu* menu)> onOpen, bool alignRight = false);
|
||||
|
||||
protected:
|
||||
void OnGeometryChanged() override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
void OnKeyDown(InputKey key) override;
|
||||
|
||||
private:
|
||||
void ShowMenu(MenubarItem* item);
|
||||
void CloseMenu();
|
||||
|
||||
MenubarItem* GetMenubarItemAt(const Point& pos);
|
||||
int GetItemIndex(MenubarItem* item);
|
||||
|
||||
std::vector<MenubarItem*> menuItems;
|
||||
int currentMenubarItem = -1;
|
||||
Menu* openMenu = nullptr;
|
||||
bool modalMode = false;
|
||||
|
||||
friend class MenubarItem;
|
||||
};
|
||||
|
||||
class MenubarItem : public Widget
|
||||
{
|
||||
public:
|
||||
MenubarItem(Menubar* menubar, std::string text, bool alignRight);
|
||||
|
||||
void SetOpenCallback(std::function<void(Menu* menu)> callback) { onOpen = std::move(callback); }
|
||||
const std::function<void(Menu* menu)>& GetOpenCallback() const { return onOpen; }
|
||||
|
||||
double GetPreferredWidth() const;
|
||||
|
||||
bool AlignRight = false;
|
||||
|
||||
protected:
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
void OnMouseLeave() override;
|
||||
|
||||
private:
|
||||
Menubar* menubar = nullptr;
|
||||
std::function<void(Menu* menu)> onOpen;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
class Menu : public Widget
|
||||
{
|
||||
public:
|
||||
Menu(Widget* parent);
|
||||
|
||||
void SetLeftPosition(const Point& globalPos);
|
||||
void SetRightPosition(const Point& globalPos);
|
||||
MenuItem* AddItem(std::shared_ptr<Image> icon, std::string text, std::function<void()> onClick = {});
|
||||
MenuItemSeparator* AddSeparator();
|
||||
|
||||
double GetPreferredWidth() const;
|
||||
double GetPreferredHeight() const;
|
||||
|
||||
void SetSelected(MenuItem* item);
|
||||
|
||||
protected:
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
std::function<void()> onCloseMenu;
|
||||
MenuItem* selectedItem = nullptr;
|
||||
|
||||
friend class Menubar;
|
||||
};
|
||||
|
||||
class MenuItem : public Widget
|
||||
{
|
||||
public:
|
||||
MenuItem(Menu* menu, std::function<void()> onClick);
|
||||
|
||||
void Click();
|
||||
|
||||
Menu* menu = nullptr;
|
||||
ImageBox* icon = nullptr;
|
||||
TextLabel* text = nullptr;
|
||||
|
||||
protected:
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
void OnMouseLeave() override;
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
private:
|
||||
std::function<void()> onClick;
|
||||
};
|
||||
|
||||
class MenuItemSeparator : public Widget
|
||||
{
|
||||
public:
|
||||
MenuItemSeparator(Widget* parent);
|
||||
|
||||
protected:
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,17 +19,14 @@ public:
|
|||
std::function<void()> OnClick;
|
||||
|
||||
protected:
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
bool OnMouseDown(const Point& pos, int key) override;
|
||||
bool OnMouseUp(const Point& pos, int key) override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
void OnMouseLeave() override;
|
||||
void OnKeyDown(EInputKey key) override;
|
||||
void OnKeyUp(EInputKey key) override;
|
||||
void OnKeyDown(InputKey key) override;
|
||||
void OnKeyUp(InputKey key) override;
|
||||
|
||||
private:
|
||||
std::string text;
|
||||
bool buttonDown = false;
|
||||
bool hot = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ public:
|
|||
std::function<void()> FuncScrollEnd;
|
||||
|
||||
protected:
|
||||
bool OnMouseDown(const Point& pos, int key) override;
|
||||
bool OnMouseUp(const Point& pos, int key) override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
void OnMouseLeave() override;
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../core/widget.h"
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class TabBar;
|
||||
class TabBarTab;
|
||||
|
|
@ -37,7 +37,6 @@ public:
|
|||
std::function<void()> OnCurrentChanged;
|
||||
|
||||
protected:
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
private:
|
||||
|
|
@ -67,7 +66,6 @@ public:
|
|||
std::function<void()> OnCurrentChanged;
|
||||
|
||||
protected:
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
private:
|
||||
|
|
@ -92,10 +90,9 @@ public:
|
|||
std::function<void()> OnClick;
|
||||
|
||||
protected:
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
void OnGeometryChanged() override;
|
||||
bool OnMouseDown(const Point& pos, int key) override;
|
||||
bool OnMouseUp(const Point& pos, int key) override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
void OnMouseLeave() override;
|
||||
|
||||
|
|
@ -116,7 +113,6 @@ public:
|
|||
Widget* GetCurrentWidget() const { return CurrentWidget; }
|
||||
|
||||
protected:
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -53,15 +53,14 @@ public:
|
|||
std::function<void()> FuncEnterPressed;
|
||||
|
||||
protected:
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
bool OnMouseDown(const Point& pos, int key) override;
|
||||
bool OnMouseDoubleclick(const Point& pos, int key) override;
|
||||
bool OnMouseUp(const Point& pos, int key) override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseDoubleclick(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnKeyChar(std::string chars) override;
|
||||
void OnKeyDown(EInputKey key) override;
|
||||
void OnKeyUp(EInputKey key) override;
|
||||
void OnKeyDown(InputKey key) override;
|
||||
void OnKeyUp(InputKey key) override;
|
||||
void OnGeometryChanged() override;
|
||||
void OnEnableChanged() override;
|
||||
void OnSetFocus() override;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "../../core/widget.h"
|
||||
|
||||
enum TextLabelAlignment
|
||||
enum class TextLabelAlignment
|
||||
{
|
||||
Left,
|
||||
Center,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,29 @@
|
|||
|
||||
#include "../../core/widget.h"
|
||||
|
||||
enum class ToolbarDirection
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
};
|
||||
|
||||
class ToolbarButton;
|
||||
|
||||
class Toolbar : public Widget
|
||||
{
|
||||
public:
|
||||
Toolbar(Widget* parent);
|
||||
~Toolbar();
|
||||
|
||||
void SetDirection(ToolbarDirection direction);
|
||||
ToolbarDirection GetDirection(ToolbarDirection) const { return direction; }
|
||||
|
||||
ToolbarButton* AddButton(std::string icon, std::string text, std::function<void()> onClicked = {});
|
||||
|
||||
protected:
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
private:
|
||||
ToolbarDirection direction = ToolbarDirection::Horizontal;
|
||||
std::vector<ToolbarButton*> buttons;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../core/widget.h"
|
||||
#include "../textlabel/textlabel.h"
|
||||
#include "../imagebox/imagebox.h"
|
||||
|
||||
class ToolbarButton : public Widget
|
||||
{
|
||||
|
|
@ -9,6 +11,24 @@ public:
|
|||
ToolbarButton(Widget* parent);
|
||||
~ToolbarButton();
|
||||
|
||||
void SetIcon(std::string icon);
|
||||
void SetText(std::string text);
|
||||
|
||||
void Click();
|
||||
|
||||
double GetPreferredWidth();
|
||||
double GetPreferredHeight();
|
||||
|
||||
std::function<void()> OnClick;
|
||||
|
||||
protected:
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
void OnMouseMove(const Point& pos) override;
|
||||
void OnMouseLeave() override;
|
||||
bool OnMouseDown(const Point& pos, InputKey key) override;
|
||||
bool OnMouseUp(const Point& pos, InputKey key) override;
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
private:
|
||||
ImageBox* image = nullptr;
|
||||
TextLabel* label = nullptr;
|
||||
};
|
||||
|
|
|
|||
12
libraries/ZWidget/include/zwidget/window/sdl2nativehandle.h
Normal file
12
libraries/ZWidget/include/zwidget/window/sdl2nativehandle.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
class SDL2NativeHandle
|
||||
{
|
||||
public:
|
||||
SDL_Window* window = nullptr;
|
||||
};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <wayland-client-protocol.h>
|
||||
|
||||
class WaylandNativeHandle
|
||||
{
|
||||
public:
|
||||
wl_display* display = nullptr;
|
||||
wl_surface* surface = nullptr;
|
||||
};
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
typedef struct HWND__* HWND;
|
||||
|
||||
class Win32NativeHandle
|
||||
{
|
||||
public:
|
||||
HWND hwnd = {};
|
||||
};
|
||||
|
|
@ -1,12 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include "../core/rect.h"
|
||||
|
||||
class Engine;
|
||||
#ifndef VULKAN_H_
|
||||
|
||||
#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
|
||||
|
||||
#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
|
||||
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;
|
||||
#else
|
||||
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
|
||||
#endif
|
||||
|
||||
VK_DEFINE_HANDLE(VkInstance)
|
||||
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR)
|
||||
|
||||
#endif
|
||||
|
||||
class Widget;
|
||||
class OpenFileDialog;
|
||||
class SaveFileDialog;
|
||||
class OpenFolderDialog;
|
||||
|
||||
enum class StandardCursor
|
||||
{
|
||||
|
|
@ -25,91 +44,83 @@ enum class StandardCursor
|
|||
wait
|
||||
};
|
||||
|
||||
enum EInputKey
|
||||
enum class InputKey : uint32_t
|
||||
{
|
||||
IK_None, IK_LeftMouse, IK_RightMouse, IK_Cancel,
|
||||
IK_MiddleMouse, IK_Unknown05, IK_Unknown06, IK_Unknown07,
|
||||
IK_Backspace, IK_Tab, IK_Unknown0A, IK_Unknown0B,
|
||||
IK_Unknown0C, IK_Enter, IK_Unknown0E, IK_Unknown0F,
|
||||
IK_Shift, IK_Ctrl, IK_Alt, IK_Pause,
|
||||
IK_CapsLock, IK_Unknown15, IK_Unknown16, IK_Unknown17,
|
||||
IK_Unknown18, IK_Unknown19, IK_Unknown1A, IK_Escape,
|
||||
IK_Unknown1C, IK_Unknown1D, IK_Unknown1E, IK_Unknown1F,
|
||||
IK_Space, IK_PageUp, IK_PageDown, IK_End,
|
||||
IK_Home, IK_Left, IK_Up, IK_Right,
|
||||
IK_Down, IK_Select, IK_Print, IK_Execute,
|
||||
IK_PrintScrn, IK_Insert, IK_Delete, IK_Help,
|
||||
IK_0, IK_1, IK_2, IK_3,
|
||||
IK_4, IK_5, IK_6, IK_7,
|
||||
IK_8, IK_9, IK_Unknown3A, IK_Unknown3B,
|
||||
IK_Unknown3C, IK_Unknown3D, IK_Unknown3E, IK_Unknown3F,
|
||||
IK_Unknown40, IK_A, IK_B, IK_C,
|
||||
IK_D, IK_E, IK_F, IK_G,
|
||||
IK_H, IK_I, IK_J, IK_K,
|
||||
IK_L, IK_M, IK_N, IK_O,
|
||||
IK_P, IK_Q, IK_R, IK_S,
|
||||
IK_T, IK_U, IK_V, IK_W,
|
||||
IK_X, IK_Y, IK_Z, IK_Unknown5B,
|
||||
IK_Unknown5C, IK_Unknown5D, IK_Unknown5E, IK_Unknown5F,
|
||||
IK_NumPad0, IK_NumPad1, IK_NumPad2, IK_NumPad3,
|
||||
IK_NumPad4, IK_NumPad5, IK_NumPad6, IK_NumPad7,
|
||||
IK_NumPad8, IK_NumPad9, IK_GreyStar, IK_GreyPlus,
|
||||
IK_Separator, IK_GreyMinus, IK_NumPadPeriod, IK_GreySlash,
|
||||
IK_F1, IK_F2, IK_F3, IK_F4,
|
||||
IK_F5, IK_F6, IK_F7, IK_F8,
|
||||
IK_F9, IK_F10, IK_F11, IK_F12,
|
||||
IK_F13, IK_F14, IK_F15, IK_F16,
|
||||
IK_F17, IK_F18, IK_F19, IK_F20,
|
||||
IK_F21, IK_F22, IK_F23, IK_F24,
|
||||
IK_Unknown88, IK_Unknown89, IK_Unknown8A, IK_Unknown8B,
|
||||
IK_Unknown8C, IK_Unknown8D, IK_Unknown8E, IK_Unknown8F,
|
||||
IK_NumLock, IK_ScrollLock, IK_Unknown92, IK_Unknown93,
|
||||
IK_Unknown94, IK_Unknown95, IK_Unknown96, IK_Unknown97,
|
||||
IK_Unknown98, IK_Unknown99, IK_Unknown9A, IK_Unknown9B,
|
||||
IK_Unknown9C, IK_Unknown9D, IK_Unknown9E, IK_Unknown9F,
|
||||
IK_LShift, IK_RShift, IK_LControl, IK_RControl,
|
||||
IK_UnknownA4, IK_UnknownA5, IK_UnknownA6, IK_UnknownA7,
|
||||
IK_UnknownA8, IK_UnknownA9, IK_UnknownAA, IK_UnknownAB,
|
||||
IK_UnknownAC, IK_UnknownAD, IK_UnknownAE, IK_UnknownAF,
|
||||
IK_UnknownB0, IK_UnknownB1, IK_UnknownB2, IK_UnknownB3,
|
||||
IK_UnknownB4, IK_UnknownB5, IK_UnknownB6, IK_UnknownB7,
|
||||
IK_UnknownB8, IK_UnknownB9, IK_Semicolon, IK_Equals,
|
||||
IK_Comma, IK_Minus, IK_Period, IK_Slash,
|
||||
IK_Tilde, IK_UnknownC1, IK_UnknownC2, IK_UnknownC3,
|
||||
IK_UnknownC4, IK_UnknownC5, IK_UnknownC6, IK_UnknownC7,
|
||||
IK_Joy1, IK_Joy2, IK_Joy3, IK_Joy4,
|
||||
IK_Joy5, IK_Joy6, IK_Joy7, IK_Joy8,
|
||||
IK_Joy9, IK_Joy10, IK_Joy11, IK_Joy12,
|
||||
IK_Joy13, IK_Joy14, IK_Joy15, IK_Joy16,
|
||||
IK_UnknownD8, IK_UnknownD9, IK_UnknownDA, IK_LeftBracket,
|
||||
IK_Backslash, IK_RightBracket, IK_SingleQuote, IK_UnknownDF,
|
||||
IK_JoyX, IK_JoyY, IK_JoyZ, IK_JoyR,
|
||||
IK_MouseX, IK_MouseY, IK_MouseZ, IK_MouseW,
|
||||
IK_JoyU, IK_JoyV, IK_UnknownEA, IK_UnknownEB,
|
||||
IK_MouseWheelUp, IK_MouseWheelDown, IK_Unknown10E, IK_Unknown10F,
|
||||
IK_JoyPovUp, IK_JoyPovDown, IK_JoyPovLeft, IK_JoyPovRight,
|
||||
IK_UnknownF4, IK_UnknownF5, IK_Attn, IK_CrSel,
|
||||
IK_ExSel, IK_ErEof, IK_Play, IK_Zoom,
|
||||
IK_NoName, IK_PA1, IK_OEMClear
|
||||
None, LeftMouse, RightMouse, Cancel,
|
||||
MiddleMouse, Unknown05, Unknown06, Unknown07,
|
||||
Backspace, Tab, Unknown0A, Unknown0B,
|
||||
Unknown0C, Enter, Unknown0E, Unknown0F,
|
||||
Shift, Ctrl, Alt, Pause,
|
||||
CapsLock, Unknown15, Unknown16, Unknown17,
|
||||
Unknown18, Unknown19, Unknown1A, Escape,
|
||||
Unknown1C, Unknown1D, Unknown1E, Unknown1F,
|
||||
Space, PageUp, PageDown, End,
|
||||
Home, Left, Up, Right,
|
||||
Down, Select, Print, Execute,
|
||||
PrintScrn, Insert, Delete, Help,
|
||||
_0, _1, _2, _3,
|
||||
_4, _5, _6, _7,
|
||||
_8, _9, Unknown3A, Unknown3B,
|
||||
Unknown3C, Unknown3D, Unknown3E, Unknown3F,
|
||||
Unknown40, A, B, C,
|
||||
D, E, F, G,
|
||||
H, I, J, K,
|
||||
L, M, N, O,
|
||||
P, Q, R, S,
|
||||
T, U, V, W,
|
||||
X, Y, Z, Unknown5B,
|
||||
Unknown5C, Unknown5D, Unknown5E, Unknown5F,
|
||||
NumPad0, NumPad1, NumPad2, NumPad3,
|
||||
NumPad4, NumPad5, NumPad6, NumPad7,
|
||||
NumPad8, NumPad9, GreyStar, GreyPlus,
|
||||
Separator, GreyMinus, NumPadPeriod, GreySlash,
|
||||
F1, F2, F3, F4,
|
||||
F5, F6, F7, F8,
|
||||
F9, F10, F11, F12,
|
||||
F13, F14, F15, F16,
|
||||
F17, F18, F19, F20,
|
||||
F21, F22, F23, F24,
|
||||
Unknown88, Unknown89, Unknown8A, Unknown8B,
|
||||
Unknown8C, Unknown8D, Unknown8E, Unknown8F,
|
||||
NumLock, ScrollLock, Unknown92, Unknown93,
|
||||
Unknown94, Unknown95, Unknown96, Unknown97,
|
||||
Unknown98, Unknown99, Unknown9A, Unknown9B,
|
||||
Unknown9C, Unknown9D, Unknown9E, Unknown9F,
|
||||
LShift, RShift, LControl, RControl,
|
||||
UnknownA4, UnknownA5, UnknownA6, UnknownA7,
|
||||
UnknownA8, UnknownA9, UnknownAA, UnknownAB,
|
||||
UnknownAC, UnknownAD, UnknownAE, UnknownAF,
|
||||
UnknownB0, UnknownB1, UnknownB2, UnknownB3,
|
||||
UnknownB4, UnknownB5, UnknownB6, UnknownB7,
|
||||
UnknownB8, UnknownB9, Semicolon, Equals,
|
||||
Comma, Minus, Period, Slash,
|
||||
Tilde, UnknownC1, UnknownC2, UnknownC3,
|
||||
UnknownC4, UnknownC5, UnknownC6, UnknownC7,
|
||||
Joy1, Joy2, Joy3, Joy4,
|
||||
Joy5, Joy6, Joy7, Joy8,
|
||||
Joy9, Joy10, Joy11, Joy12,
|
||||
Joy13, Joy14, Joy15, Joy16,
|
||||
UnknownD8, UnknownD9, UnknownDA, LeftBracket,
|
||||
Backslash, RightBracket, SingleQuote, UnknownDF,
|
||||
JoyX, JoyY, JoyZ, JoyR,
|
||||
MouseX, MouseY, MouseZ, MouseW,
|
||||
JoyU, JoyV, UnknownEA, UnknownEB,
|
||||
MouseWheelUp, MouseWheelDown, Unknown10E, Unknown10F,
|
||||
JoyPovUp, JoyPovDown, JoyPovLeft, JoyPovRight,
|
||||
UnknownF4, UnknownF5, Attn, CrSel,
|
||||
ExSel, ErEof, Play, Zoom,
|
||||
NoName, PA1, OEMClear
|
||||
};
|
||||
|
||||
enum EInputType
|
||||
enum class RenderAPI
|
||||
{
|
||||
IST_None,
|
||||
IST_Press,
|
||||
IST_Hold,
|
||||
IST_Release,
|
||||
IST_Axis
|
||||
};
|
||||
|
||||
class KeyEvent
|
||||
{
|
||||
public:
|
||||
EInputKey Key;
|
||||
bool Ctrl = false;
|
||||
bool Alt = false;
|
||||
bool Shift = false;
|
||||
Point MousePos = Point(0.0, 0.0);
|
||||
Unspecified,
|
||||
Bitmap,
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
D3D11,
|
||||
D3D12,
|
||||
Metal
|
||||
};
|
||||
|
||||
class DisplayWindow;
|
||||
|
|
@ -119,14 +130,15 @@ class DisplayWindowHost
|
|||
public:
|
||||
virtual void OnWindowPaint() = 0;
|
||||
virtual void OnWindowMouseMove(const Point& pos) = 0;
|
||||
virtual void OnWindowMouseDown(const Point& pos, EInputKey key) = 0;
|
||||
virtual void OnWindowMouseDoubleclick(const Point& pos, EInputKey key) = 0;
|
||||
virtual void OnWindowMouseUp(const Point& pos, EInputKey key) = 0;
|
||||
virtual void OnWindowMouseWheel(const Point& pos, EInputKey key) = 0;
|
||||
virtual void OnWindowMouseLeave() = 0;
|
||||
virtual void OnWindowMouseDown(const Point& pos, InputKey key) = 0;
|
||||
virtual void OnWindowMouseDoubleclick(const Point& pos, InputKey key) = 0;
|
||||
virtual void OnWindowMouseUp(const Point& pos, InputKey key) = 0;
|
||||
virtual void OnWindowMouseWheel(const Point& pos, InputKey key) = 0;
|
||||
virtual void OnWindowRawMouseMove(int dx, int dy) = 0;
|
||||
virtual void OnWindowKeyChar(std::string chars) = 0;
|
||||
virtual void OnWindowKeyDown(EInputKey key) = 0;
|
||||
virtual void OnWindowKeyUp(EInputKey key) = 0;
|
||||
virtual void OnWindowKeyDown(InputKey key) = 0;
|
||||
virtual void OnWindowKeyUp(InputKey key) = 0;
|
||||
virtual void OnWindowGeometryChanged() = 0;
|
||||
virtual void OnWindowClose() = 0;
|
||||
virtual void OnWindowActivated() = 0;
|
||||
|
|
@ -137,7 +149,7 @@ public:
|
|||
class DisplayWindow
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost);
|
||||
static std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI);
|
||||
|
||||
static void ProcessEvents();
|
||||
static void RunLoop();
|
||||
|
|
@ -158,6 +170,7 @@ public:
|
|||
virtual void ShowMaximized() = 0;
|
||||
virtual void ShowMinimized() = 0;
|
||||
virtual void ShowNormal() = 0;
|
||||
virtual bool IsWindowFullscreen() = 0;
|
||||
virtual void Hide() = 0;
|
||||
virtual void Activate() = 0;
|
||||
virtual void ShowCursor(bool enable) = 0;
|
||||
|
|
@ -166,7 +179,7 @@ public:
|
|||
virtual void CaptureMouse() = 0;
|
||||
virtual void ReleaseMouseCapture() = 0;
|
||||
virtual void Update() = 0;
|
||||
virtual bool GetKeyState(EInputKey key) = 0;
|
||||
virtual bool GetKeyState(InputKey key) = 0;
|
||||
|
||||
virtual void SetCursor(StandardCursor cursor) = 0;
|
||||
|
||||
|
|
@ -176,6 +189,9 @@ public:
|
|||
virtual int GetPixelHeight() const = 0;
|
||||
virtual double GetDpiScale() const = 0;
|
||||
|
||||
virtual Point MapFromGlobal(const Point& pos) const = 0;
|
||||
virtual Point MapToGlobal(const Point& pos) const = 0;
|
||||
|
||||
virtual void SetBorderColor(uint32_t bgra8) = 0;
|
||||
virtual void SetCaptionColor(uint32_t bgra8) = 0;
|
||||
virtual void SetCaptionTextColor(uint32_t bgra8) = 0;
|
||||
|
|
@ -184,4 +200,44 @@ public:
|
|||
|
||||
virtual std::string GetClipboardText() = 0;
|
||||
virtual void SetClipboardText(const std::string& text) = 0;
|
||||
|
||||
virtual void* GetNativeHandle() = 0;
|
||||
|
||||
virtual std::vector<std::string> GetVulkanInstanceExtensions() = 0;
|
||||
virtual VkSurfaceKHR CreateVulkanSurface(VkInstance instance) = 0;
|
||||
};
|
||||
|
||||
class DisplayBackend
|
||||
{
|
||||
public:
|
||||
static DisplayBackend* Get();
|
||||
static void Set(std::unique_ptr<DisplayBackend> instance);
|
||||
|
||||
static std::unique_ptr<DisplayBackend> TryCreateWin32();
|
||||
static std::unique_ptr<DisplayBackend> TryCreateSDL2();
|
||||
static std::unique_ptr<DisplayBackend> TryCreateX11();
|
||||
static std::unique_ptr<DisplayBackend> TryCreateWayland();
|
||||
|
||||
static std::unique_ptr<DisplayBackend> TryCreateBackend();
|
||||
|
||||
virtual ~DisplayBackend() = default;
|
||||
|
||||
virtual bool IsWin32() { return false; }
|
||||
virtual bool IsSDL2() { return false; }
|
||||
virtual bool IsX11() { return false; }
|
||||
virtual bool IsWayland() { return false; }
|
||||
|
||||
virtual std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI) = 0;
|
||||
virtual void ProcessEvents() = 0;
|
||||
virtual void RunLoop() = 0;
|
||||
virtual void ExitLoop() = 0;
|
||||
|
||||
virtual void* StartTimer(int timeoutMilliseconds, std::function<void()> onTimer) = 0;
|
||||
virtual void StopTimer(void* timerID) = 0;
|
||||
|
||||
virtual Size GetScreenSize() = 0;
|
||||
|
||||
virtual std::unique_ptr<OpenFileDialog> CreateOpenFileDialog(DisplayWindow* owner);
|
||||
virtual std::unique_ptr<SaveFileDialog> CreateSaveFileDialog(DisplayWindow* owner);
|
||||
virtual std::unique_ptr<OpenFolderDialog> CreateOpenFolderDialog(DisplayWindow* owner);
|
||||
};
|
||||
|
|
|
|||
13
libraries/ZWidget/include/zwidget/window/x11nativehandle.h
Normal file
13
libraries/ZWidget/include/zwidget/window/x11nativehandle.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
typedef struct _XDisplay Display;
|
||||
typedef unsigned long XID;
|
||||
typedef unsigned long VisualID;
|
||||
typedef XID Window;
|
||||
|
||||
class X11NativeHandle
|
||||
{
|
||||
public:
|
||||
Display* display = nullptr;
|
||||
Window window = 0;
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -331,7 +331,7 @@ static float nsvg__normalize(float *x, float* y)
|
|||
}
|
||||
|
||||
static float nsvg__absf(float x) { return x < 0 ? -x : x; }
|
||||
static float nsvg__roundf(float x) { return (x >= 0) ? floorf(x + 0.5) : ceilf(x - 0.5); }
|
||||
static float nsvg__roundf(float x) { return (x >= 0) ? (float)floorf(x + 0.5f) : (float)ceilf(x - 0.5f); }
|
||||
|
||||
static void nsvg__flattenCubicBez(NSVGrasterizer* r,
|
||||
float x1, float y1, float x2, float y2,
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ void PathFillRasterizer::Rasterize(const PathFillDesc& path, uint8_t* dest, int
|
|||
height = block_height;
|
||||
|
||||
scanlines.resize(block_height);
|
||||
first_scanline = scanlines.size();
|
||||
first_scanline = (int)scanlines.size();
|
||||
last_scanline = 0;
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +264,7 @@ void PathFillRasterizer::Clear()
|
|||
}
|
||||
}
|
||||
|
||||
first_scanline = scanlines.size();
|
||||
first_scanline = (int)scanlines.size();
|
||||
last_scanline = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
383
libraries/ZWidget/src/core/theme.cpp
Normal file
383
libraries/ZWidget/src/core/theme.cpp
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
|
||||
#include "core/theme.h"
|
||||
#include "core/widget.h"
|
||||
#include "core/canvas.h"
|
||||
|
||||
void WidgetStyle::SetBool(const std::string& state, const std::string& propertyName, bool value)
|
||||
{
|
||||
StyleProperties[state][propertyName] = value;
|
||||
}
|
||||
|
||||
void WidgetStyle::SetInt(const std::string& state, const std::string& propertyName, int value)
|
||||
{
|
||||
StyleProperties[state][propertyName] = value;
|
||||
}
|
||||
|
||||
void WidgetStyle::SetDouble(const std::string& state, const std::string& propertyName, double value)
|
||||
{
|
||||
StyleProperties[state][propertyName] = value;
|
||||
}
|
||||
|
||||
void WidgetStyle::SetString(const std::string& state, const std::string& propertyName, const std::string& value)
|
||||
{
|
||||
StyleProperties[state][propertyName] = value;
|
||||
}
|
||||
|
||||
void WidgetStyle::SetColor(const std::string& state, const std::string& propertyName, const Colorf& value)
|
||||
{
|
||||
StyleProperties[state][propertyName] = value;
|
||||
}
|
||||
|
||||
const WidgetStyle::PropertyVariant* WidgetStyle::FindProperty(const std::string& state, const std::string& propertyName) const
|
||||
{
|
||||
const WidgetStyle* style = this;
|
||||
do
|
||||
{
|
||||
// Look for property in the specific state
|
||||
auto stateIt = style->StyleProperties.find(state);
|
||||
if (stateIt != style->StyleProperties.end())
|
||||
{
|
||||
auto it = stateIt->second.find(propertyName);
|
||||
if (it != stateIt->second.end())
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
// Fall back to the widget main style
|
||||
if (state != std::string())
|
||||
{
|
||||
stateIt = style->StyleProperties.find(std::string());
|
||||
if (stateIt != style->StyleProperties.end())
|
||||
{
|
||||
auto it = stateIt->second.find(propertyName);
|
||||
if (it != stateIt->second.end())
|
||||
return &it->second;
|
||||
}
|
||||
}
|
||||
|
||||
style = style->ParentStyle;
|
||||
} while (style);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool WidgetStyle::GetBool(const std::string& state, const std::string& propertyName) const
|
||||
{
|
||||
const PropertyVariant* prop = FindProperty(state, propertyName);
|
||||
return prop ? std::get<bool>(*prop) : false;
|
||||
}
|
||||
|
||||
int WidgetStyle::GetInt(const std::string& state, const std::string& propertyName) const
|
||||
{
|
||||
const PropertyVariant* prop = FindProperty(state, propertyName);
|
||||
return prop ? std::get<int>(*prop) : 0;
|
||||
}
|
||||
|
||||
double WidgetStyle::GetDouble(const std::string& state, const std::string& propertyName) const
|
||||
{
|
||||
const PropertyVariant* prop = FindProperty(state, propertyName);
|
||||
return prop ? std::get<double>(*prop) : 0.0;
|
||||
}
|
||||
|
||||
std::string WidgetStyle::GetString(const std::string& state, const std::string& propertyName) const
|
||||
{
|
||||
const PropertyVariant* prop = FindProperty(state, propertyName);
|
||||
return prop ? std::get<std::string>(*prop) : std::string();
|
||||
}
|
||||
|
||||
Colorf WidgetStyle::GetColor(const std::string& state, const std::string& propertyName) const
|
||||
{
|
||||
const PropertyVariant* prop = FindProperty(state, propertyName);
|
||||
return prop ? std::get<Colorf>(*prop) : Colorf::transparent();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void BasicWidgetStyle::Paint(Widget* widget, Canvas* canvas, Size size)
|
||||
{
|
||||
Colorf bgcolor = widget->GetStyleColor("background-color");
|
||||
if (bgcolor.a > 0.0f)
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, size.width, size.height), bgcolor);
|
||||
|
||||
Colorf borderleft = widget->GetStyleColor("border-left-color");
|
||||
Colorf bordertop = widget->GetStyleColor("border-top-color");
|
||||
Colorf borderright = widget->GetStyleColor("border-right-color");
|
||||
Colorf borderbottom = widget->GetStyleColor("border-bottom-color");
|
||||
|
||||
double borderwidth = widget->GridFitSize(1.0);
|
||||
|
||||
if (bordertop.a > 0.0f)
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, size.width, borderwidth), bordertop);
|
||||
if (borderbottom.a > 0.0f)
|
||||
canvas->fillRect(Rect::xywh(0.0, size.height - borderwidth, size.width, borderwidth), borderbottom);
|
||||
if (borderleft.a > 0.0f)
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, borderwidth, size.height), borderleft);
|
||||
if (borderright.a > 0.0f)
|
||||
canvas->fillRect(Rect::xywh(size.width - borderwidth, 0.0, borderwidth, size.height), borderright);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::unique_ptr<WidgetTheme> CurrentTheme;
|
||||
|
||||
WidgetStyle* WidgetTheme::RegisterStyle(std::unique_ptr<WidgetStyle> widgetStyle, const std::string& widgetClass)
|
||||
{
|
||||
auto& style = Styles[widgetClass];
|
||||
style = std::move(widgetStyle);
|
||||
return style.get();
|
||||
}
|
||||
|
||||
WidgetStyle* WidgetTheme::GetStyle(const std::string& widgetClass)
|
||||
{
|
||||
auto it = Styles.find(widgetClass);
|
||||
return it != Styles.end() ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
void WidgetTheme::SetTheme(std::unique_ptr<WidgetTheme> theme)
|
||||
{
|
||||
CurrentTheme = std::move(theme);
|
||||
}
|
||||
|
||||
WidgetTheme* WidgetTheme::GetTheme()
|
||||
{
|
||||
return CurrentTheme.get();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DarkWidgetTheme::DarkWidgetTheme()
|
||||
{
|
||||
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 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 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");
|
||||
auto toolbar = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "toolbar");
|
||||
auto toolbarbutton = RegisterStyle(std::make_unique<BasicWidgetStyle>(widget), "toolbarbutton");
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
scrollbar->SetColor("track-color", Colorf::fromRgba8(33, 33, 33));
|
||||
scrollbar->SetColor("thumb-color", Colorf::fromRgba8(58, 58, 58));
|
||||
|
||||
tabbar->SetDouble("noncontent-left", 20.0);
|
||||
tabbar->SetDouble("noncontent-right", 20.0);
|
||||
tabbar->SetColor("background-color", Colorf::fromRgba8(38, 38, 38));
|
||||
|
||||
tabbar_tab->SetDouble("noncontent-left", 15.0);
|
||||
tabbar_tab->SetDouble("noncontent-right", 15.0);
|
||||
tabbar_tab->SetColor("hover", "background-color", Colorf::fromRgba8(45, 45, 45));
|
||||
tabbar_tab->SetColor("active", "background-color", Colorf::fromRgba8(51, 51, 51));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
toolbarbutton->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78));
|
||||
toolbarbutton->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88));
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
menuitem->SetColor("hover", "background-color", Colorf::fromRgba8(78, 78, 78));
|
||||
menuitem->SetColor("down", "background-color", Colorf::fromRgba8(88, 88, 88));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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 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 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");
|
||||
|
||||
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));
|
||||
|
||||
scrollbar->SetColor("track-color", Colorf::fromRgba8(210, 210, 220));
|
||||
scrollbar->SetColor("thumb-color", Colorf::fromRgba8(180, 180, 180));
|
||||
|
||||
tabbar->SetDouble("noncontent-left", 20.0);
|
||||
tabbar->SetDouble("noncontent-right", 20.0);
|
||||
tabbar->SetColor("background-color", Colorf::fromRgba8(220, 220, 220));
|
||||
|
||||
tabbar_tab->SetDouble("noncontent-left", 15.0);
|
||||
tabbar_tab->SetDouble("noncontent-right", 15.0);
|
||||
tabbar_tab->SetColor("hover", "background-color", Colorf::fromRgba8(210, 210, 210));
|
||||
tabbar_tab->SetColor("active", "background-color", Colorf::fromRgba8(240, 240, 240));
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ Timer::Timer(Widget* owner) : OwnerObj(owner)
|
|||
|
||||
Timer::~Timer()
|
||||
{
|
||||
Stop();
|
||||
|
||||
if (PrevTimerObj)
|
||||
PrevTimerObj->NextTimerObj = NextTimerObj;
|
||||
if (NextTimerObj)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#include <fstream>
|
||||
#endif
|
||||
|
||||
TrueTypeFont::TrueTypeFont(std::shared_ptr<TrueTypeFontFileData>& initdata, int ttcFontIndex) : data(initdata)
|
||||
TrueTypeFont::TrueTypeFont(std::shared_ptr<TrueTypeFontFileData> initdata, int ttcFontIndex) : data(std::move(initdata))
|
||||
{
|
||||
if (data->size() > 0x7fffffff)
|
||||
throw std::runtime_error("TTF file is larger than 2 gigabytes!");
|
||||
|
|
@ -23,7 +23,7 @@ TrueTypeFont::TrueTypeFont(std::shared_ptr<TrueTypeFontFileData>& initdata, int
|
|||
if (memcmp(versionTag.data(), "ttcf", 4) == 0) // TTC header
|
||||
{
|
||||
ttcHeader.Load(reader);
|
||||
if (ttcFontIndex >= ttcHeader.numFonts)
|
||||
if (ttcFontIndex >= (int)ttcHeader.numFonts)
|
||||
throw std::runtime_error("TTC font index out of bounds");
|
||||
reader.Seek(ttcHeader.tableDirectoryOffsets[ttcFontIndex]);
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@ TrueTypeGlyph TrueTypeFont::LoadGlyph(uint32_t glyphIndex, double height) const
|
|||
path.fill_mode = PathFillMode::winding;
|
||||
|
||||
int startPoint = 0;
|
||||
int numberOfContours = g.endPtsOfContours.size();
|
||||
int numberOfContours = (int)g.endPtsOfContours.size();
|
||||
for (int i = 0; i < numberOfContours; i++)
|
||||
{
|
||||
int endPoint = g.endPtsOfContours[i];
|
||||
|
|
@ -310,7 +310,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
|
||||
if (numberOfContours > 0) // Simple glyph
|
||||
{
|
||||
int pointsOffset = g.points.size();
|
||||
int pointsOffset = (int)g.points.size();
|
||||
for (ttf_uint16 i = 0; i < numberOfContours; i++)
|
||||
g.endPtsOfContours.push_back(pointsOffset + reader.ReadUInt16());
|
||||
|
||||
|
|
@ -339,15 +339,15 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
if (g.flags[i] & TTF_X_SHORT_VECTOR)
|
||||
{
|
||||
ttf_int16 x = reader.ReadUInt8();
|
||||
g.points[i].x = (g.flags[i] & TTF_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) ? x : -x;
|
||||
g.points[i].x = (float)((g.flags[i] & TTF_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) ? x : -x);
|
||||
}
|
||||
else if (g.flags[i] & TTF_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)
|
||||
{
|
||||
g.points[i].x = 0;
|
||||
g.points[i].x = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
g.points[i].x = reader.ReadInt16();
|
||||
g.points[i].x = (float)reader.ReadInt16();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -356,15 +356,15 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
if (g.flags[i] & TTF_Y_SHORT_VECTOR)
|
||||
{
|
||||
ttf_int16 y = reader.ReadUInt8();
|
||||
g.points[i].y = (g.flags[i] & TTF_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) ? y : -y;
|
||||
g.points[i].y = (float)((g.flags[i] & TTF_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) ? y : -y);
|
||||
}
|
||||
else if (g.flags[i] & TTF_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)
|
||||
{
|
||||
g.points[i].y = 0;
|
||||
g.points[i].y = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
g.points[i].y = reader.ReadInt16();
|
||||
g.points[i].y = (float)reader.ReadInt16();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -380,7 +380,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
if (compositeDepth == 8)
|
||||
throw std::runtime_error("Composite glyph recursion exceeded");
|
||||
|
||||
int parentPointsOffset = g.points.size();
|
||||
int parentPointsOffset = (int)g.points.size();
|
||||
|
||||
bool weHaveInstructions = false;
|
||||
while (true)
|
||||
|
|
@ -420,7 +420,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
bool transform = true;
|
||||
if (flags & TTF_WE_HAVE_A_SCALE)
|
||||
{
|
||||
ttf_F2DOT14 scale = F2DOT14_ToFloat(reader.ReadF2DOT14());
|
||||
float scale = F2DOT14_ToFloat(reader.ReadF2DOT14());
|
||||
mat2x2[0] = scale;
|
||||
mat2x2[1] = 0;
|
||||
mat2x2[2] = 0;
|
||||
|
|
@ -445,7 +445,7 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
transform = false;
|
||||
}
|
||||
|
||||
int childPointsOffset = g.points.size();
|
||||
int childPointsOffset = (int)g.points.size();
|
||||
LoadGlyph(g, childGlyphIndex, compositeDepth + 1);
|
||||
|
||||
if (transform)
|
||||
|
|
@ -463,8 +463,8 @@ void TrueTypeFont::LoadGlyph(TTF_SimpleGlyph& g, uint32_t glyphIndex, int compos
|
|||
|
||||
if (flags & TTF_ARGS_ARE_XY_VALUES)
|
||||
{
|
||||
dx = argument1;
|
||||
dy = argument2;
|
||||
dx = (float)argument1;
|
||||
dy = (float)argument2;
|
||||
|
||||
// Spec states we must fall back to TTF_UNSCALED_COMPONENT_OFFSET if both flags are set
|
||||
if ((flags & (TTF_SCALED_COMPONENT_OFFSET | TTF_UNSCALED_COMPONENT_OFFSET)) == TTF_SCALED_COMPONENT_OFFSET)
|
||||
|
|
@ -976,8 +976,8 @@ void TTF_NamingTable::Load(TrueTypeFileReader& reader)
|
|||
for (ttf_uint16 i = 0; i < langTagCount; i++)
|
||||
{
|
||||
LangTagRecord record;
|
||||
ttf_uint16 length;
|
||||
ttf_Offset16 langTagOffset;
|
||||
record.length = reader.ReadUInt16();
|
||||
record.langTagOffset = reader.ReadOffset16();
|
||||
langTagRecord.push_back(record);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ public:
|
|||
class TrueTypeFontFileData
|
||||
{
|
||||
public:
|
||||
TrueTypeFontFileData(std::vector<uint8_t>& data) : dataVector(std::move(data))
|
||||
TrueTypeFontFileData(std::vector<uint8_t> data) : dataVector(std::move(data))
|
||||
{
|
||||
dataPtr = dataVector.data();
|
||||
dataSize = dataVector.size();
|
||||
|
|
@ -491,7 +491,7 @@ private:
|
|||
class TrueTypeFont
|
||||
{
|
||||
public:
|
||||
TrueTypeFont(std::shared_ptr<TrueTypeFontFileData>& data, int ttcFontIndex = 0);
|
||||
TrueTypeFont(std::shared_ptr<TrueTypeFontFileData> data, int ttcFontIndex = 0);
|
||||
|
||||
static std::vector<TTCFontName> GetFontNames(const std::shared_ptr<TrueTypeFontFileData>& data);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,31 @@
|
|||
#include "core/widget.h"
|
||||
#include "core/timer.h"
|
||||
#include "core/colorf.h"
|
||||
#include "core/theme.h"
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
Widget::Widget(Widget* parent, WidgetType type) : Type(type)
|
||||
Widget::Widget(Widget* parent, WidgetType type, RenderAPI renderAPI) : Type(type)
|
||||
{
|
||||
if (type != WidgetType::Child)
|
||||
{
|
||||
DispWindow = DisplayWindow::Create(this);
|
||||
DispCanvas = Canvas::create(DispWindow.get());
|
||||
Widget* owner = parent ? parent->Window() : nullptr;
|
||||
DispWindow = DisplayWindow::Create(this, type == WidgetType::Popup, owner ? owner->DispWindow.get() : nullptr, renderAPI);
|
||||
if (renderAPI == RenderAPI::Unspecified || renderAPI == RenderAPI::Bitmap)
|
||||
{
|
||||
DispCanvas = Canvas::create();
|
||||
DispCanvas->attach(DispWindow.get());
|
||||
}
|
||||
SetStyleState("root");
|
||||
|
||||
SetWindowBackground(GetStyleColor("window-background"));
|
||||
if (GetStyleColor("window-border").a > 0.0f)
|
||||
SetWindowBorderColor(GetStyleColor("window-border"));
|
||||
if (GetStyleColor("window-caption-color").a > 0.0f)
|
||||
SetWindowCaptionColor(GetStyleColor("window-caption-color"));
|
||||
if (GetStyleColor("window-caption-text-color").a > 0.0f)
|
||||
SetWindowCaptionTextColor(GetStyleColor("window-caption-text-color"));
|
||||
}
|
||||
|
||||
SetParent(parent);
|
||||
|
|
@ -17,6 +34,9 @@ Widget::Widget(Widget* parent, WidgetType type) : Type(type)
|
|||
|
||||
Widget::~Widget()
|
||||
{
|
||||
if (DispCanvas)
|
||||
DispCanvas->detach();
|
||||
|
||||
while (LastChildObj)
|
||||
delete LastChildObj;
|
||||
|
||||
|
|
@ -26,6 +46,17 @@ Widget::~Widget()
|
|||
DetachFromParent();
|
||||
}
|
||||
|
||||
void Widget::SetCanvas(std::unique_ptr<Canvas> canvas)
|
||||
{
|
||||
if (DispWindow)
|
||||
{
|
||||
if (DispCanvas)
|
||||
DispCanvas->detach();
|
||||
DispCanvas = std::move(canvas);
|
||||
DispCanvas->attach(DispWindow.get());
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::SetParent(Widget* newParent)
|
||||
{
|
||||
if (ParentObj != newParent)
|
||||
|
|
@ -138,10 +169,10 @@ Rect Widget::GetFrameGeometry() const
|
|||
|
||||
void Widget::SetNoncontentSizes(double left, double top, double right, double bottom)
|
||||
{
|
||||
Noncontent.Left = left;
|
||||
Noncontent.Top = top;
|
||||
Noncontent.Right = right;
|
||||
Noncontent.Bottom = bottom;
|
||||
SetStyleDouble("noncontent-left", left);
|
||||
SetStyleDouble("noncontent-top", top);
|
||||
SetStyleDouble("noncontent-right", right);
|
||||
SetStyleDouble("noncontent-bottom", bottom);
|
||||
}
|
||||
|
||||
void Widget::SetFrameGeometry(const Rect& geometry)
|
||||
|
|
@ -149,14 +180,18 @@ void Widget::SetFrameGeometry(const Rect& geometry)
|
|||
if (Type == WidgetType::Child)
|
||||
{
|
||||
FrameGeometry = geometry;
|
||||
double left = FrameGeometry.left() + Noncontent.Left;
|
||||
double top = FrameGeometry.top() + Noncontent.Top;
|
||||
double right = FrameGeometry.right() - Noncontent.Right;
|
||||
double bottom = FrameGeometry.bottom() - Noncontent.Bottom;
|
||||
double left = FrameGeometry.left() + GetNoncontentLeft();
|
||||
double top = FrameGeometry.top() + GetNoncontentTop();
|
||||
double right = FrameGeometry.right() - GetNoncontentRight();
|
||||
double bottom = FrameGeometry.bottom() - GetNoncontentBottom();
|
||||
left = std::min(left, FrameGeometry.right());
|
||||
top = std::min(top, FrameGeometry.bottom());
|
||||
right = std::max(right, FrameGeometry.left());
|
||||
bottom = std::max(bottom, FrameGeometry.top());
|
||||
left = GridFitPoint(left);
|
||||
top = GridFitPoint(top);
|
||||
right = GridFitPoint(right);
|
||||
bottom = GridFitPoint(bottom);
|
||||
ContentGeometry = Rect::ltrb(left, top, right, bottom);
|
||||
OnGeometryChanged();
|
||||
}
|
||||
|
|
@ -187,6 +222,15 @@ void Widget::ShowFullscreen()
|
|||
}
|
||||
}
|
||||
|
||||
bool Widget::IsFullscreen()
|
||||
{
|
||||
if (Type != WidgetType::Child)
|
||||
{
|
||||
return DispWindow->IsWindowFullscreen();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Widget::ShowMaximized()
|
||||
{
|
||||
if (Type != WidgetType::Child)
|
||||
|
|
@ -290,9 +334,12 @@ void Widget::Update()
|
|||
void Widget::Repaint()
|
||||
{
|
||||
Widget* w = Window();
|
||||
w->DispCanvas->begin(WindowBackground);
|
||||
w->Paint(w->DispCanvas.get());
|
||||
w->DispCanvas->end();
|
||||
if (w->DispCanvas)
|
||||
{
|
||||
w->DispCanvas->begin(WindowBackground);
|
||||
w->Paint(w->DispCanvas.get());
|
||||
w->DispCanvas->end();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::Paint(Canvas* canvas)
|
||||
|
|
@ -316,7 +363,16 @@ void Widget::Paint(Canvas* canvas)
|
|||
canvas->popClip();
|
||||
}
|
||||
|
||||
bool Widget::GetKeyState(EInputKey key)
|
||||
void Widget::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
WidgetStyle* style = WidgetTheme::GetTheme()->GetStyle(StyleClass);
|
||||
if (style)
|
||||
{
|
||||
style->Paint(this, canvas, GetFrameGeometry().size());
|
||||
}
|
||||
}
|
||||
|
||||
bool Widget::GetKeyState(InputKey key)
|
||||
{
|
||||
Widget* window = Window();
|
||||
return window ? window->DispWindow->GetKeyState(key) : false;
|
||||
|
|
@ -403,7 +459,7 @@ void Widget::SetCursor(StandardCursor cursor)
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::CaptureMouse()
|
||||
void Widget::SetPointerCapture()
|
||||
{
|
||||
Widget* w = Window();
|
||||
if (w && w->CaptureWidget != this)
|
||||
|
|
@ -413,7 +469,7 @@ void Widget::CaptureMouse()
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::ReleaseMouseCapture()
|
||||
void Widget::ReleasePointerCapture()
|
||||
{
|
||||
Widget* w = Window();
|
||||
if (w && w->CaptureWidget != nullptr)
|
||||
|
|
@ -423,6 +479,24 @@ void Widget::ReleaseMouseCapture()
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::SetModalCapture()
|
||||
{
|
||||
Widget* w = Window();
|
||||
if (w && w->CaptureWidget != this)
|
||||
{
|
||||
w->CaptureWidget = this;
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::ReleaseModalCapture()
|
||||
{
|
||||
Widget* w = Window();
|
||||
if (w && w->CaptureWidget != nullptr)
|
||||
{
|
||||
w->CaptureWidget = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Widget::GetClipboardText()
|
||||
{
|
||||
Widget* w = Window();
|
||||
|
|
@ -439,12 +513,12 @@ void Widget::SetClipboardText(const std::string& text)
|
|||
w->DispWindow->SetClipboardText(text);
|
||||
}
|
||||
|
||||
Widget* Widget::Window()
|
||||
Widget* Widget::Window() const
|
||||
{
|
||||
for (Widget* w = this; w != nullptr; w = w->Parent())
|
||||
for (const Widget* w = this; w != nullptr; w = w->Parent())
|
||||
{
|
||||
if (w->DispWindow)
|
||||
return w;
|
||||
return const_cast<Widget*>(w);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -459,11 +533,29 @@ Canvas* Widget::GetCanvas() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool Widget::IsParent(const Widget* w) const
|
||||
{
|
||||
while (w)
|
||||
{
|
||||
w = w->Parent();
|
||||
if (w == this)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::IsChild(const Widget* w) const
|
||||
{
|
||||
if (!w)
|
||||
return false;
|
||||
return w->IsParent(this);
|
||||
}
|
||||
|
||||
Widget* Widget::ChildAt(const Point& pos)
|
||||
{
|
||||
for (Widget* cur = LastChild(); cur != nullptr; cur = cur->PrevSibling())
|
||||
{
|
||||
if (!cur->HiddenFlag && cur->FrameGeometry.contains(pos))
|
||||
if (cur->Type == WidgetType::Child && !cur->HiddenFlag && cur->FrameGeometry.contains(pos))
|
||||
{
|
||||
Widget* cur2 = cur->ChildAt(pos - cur->ContentGeometry.topLeft());
|
||||
return cur2 ? cur2 : cur;
|
||||
|
|
@ -491,7 +583,7 @@ Point Widget::MapFromGlobal(const Point& pos) const
|
|||
{
|
||||
if (cur->DispWindow)
|
||||
{
|
||||
return p - cur->GetFrameGeometry().topLeft();
|
||||
return cur->DispWindow->MapFromGlobal(p);
|
||||
}
|
||||
p -= cur->ContentGeometry.topLeft();
|
||||
}
|
||||
|
|
@ -517,7 +609,7 @@ Point Widget::MapToGlobal(const Point& pos) const
|
|||
{
|
||||
if (cur->DispWindow)
|
||||
{
|
||||
return cur->GetFrameGeometry().topLeft() + p;
|
||||
return cur->DispWindow->MapToGlobal(p);
|
||||
}
|
||||
p += cur->ContentGeometry.topLeft();
|
||||
}
|
||||
|
|
@ -570,7 +662,19 @@ void Widget::OnWindowMouseMove(const Point& pos)
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::OnWindowMouseDown(const Point& pos, EInputKey key)
|
||||
void Widget::OnWindowMouseLeave()
|
||||
{
|
||||
if (HoverWidget)
|
||||
{
|
||||
for (Widget* w = HoverWidget; w; w = w->Parent())
|
||||
{
|
||||
w->OnMouseLeave();
|
||||
}
|
||||
HoverWidget = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::OnWindowMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
if (CaptureWidget)
|
||||
{
|
||||
|
|
@ -591,7 +695,7 @@ void Widget::OnWindowMouseDown(const Point& pos, EInputKey key)
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::OnWindowMouseDoubleclick(const Point& pos, EInputKey key)
|
||||
void Widget::OnWindowMouseDoubleclick(const Point& pos, InputKey key)
|
||||
{
|
||||
if (CaptureWidget)
|
||||
{
|
||||
|
|
@ -612,7 +716,7 @@ void Widget::OnWindowMouseDoubleclick(const Point& pos, EInputKey key)
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::OnWindowMouseUp(const Point& pos, EInputKey key)
|
||||
void Widget::OnWindowMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
if (CaptureWidget)
|
||||
{
|
||||
|
|
@ -633,7 +737,7 @@ void Widget::OnWindowMouseUp(const Point& pos, EInputKey key)
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::OnWindowMouseWheel(const Point& pos, EInputKey key)
|
||||
void Widget::OnWindowMouseWheel(const Point& pos, InputKey key)
|
||||
{
|
||||
if (CaptureWidget)
|
||||
{
|
||||
|
|
@ -672,13 +776,13 @@ void Widget::OnWindowKeyChar(std::string chars)
|
|||
FocusWidget->OnKeyChar(chars);
|
||||
}
|
||||
|
||||
void Widget::OnWindowKeyDown(EInputKey key)
|
||||
void Widget::OnWindowKeyDown(InputKey key)
|
||||
{
|
||||
if (FocusWidget)
|
||||
FocusWidget->OnKeyDown(key);
|
||||
}
|
||||
|
||||
void Widget::OnWindowKeyUp(EInputKey key)
|
||||
void Widget::OnWindowKeyUp(InputKey key)
|
||||
{
|
||||
if (FocusWidget)
|
||||
FocusWidget->OnKeyUp(key);
|
||||
|
|
@ -686,9 +790,21 @@ void Widget::OnWindowKeyUp(EInputKey key)
|
|||
|
||||
void Widget::OnWindowGeometryChanged()
|
||||
{
|
||||
if (!DispWindow)
|
||||
return;
|
||||
Size size = DispWindow->GetClientSize();
|
||||
FrameGeometry = Rect::xywh(0.0, 0.0, size.width, size.height);
|
||||
ContentGeometry = FrameGeometry;
|
||||
|
||||
double left = FrameGeometry.left() + GetNoncontentLeft();
|
||||
double top = FrameGeometry.top() + GetNoncontentTop();
|
||||
double right = FrameGeometry.right() - GetNoncontentRight();
|
||||
double bottom = FrameGeometry.bottom() - GetNoncontentBottom();
|
||||
left = std::min(left, FrameGeometry.right());
|
||||
top = std::min(top, FrameGeometry.bottom());
|
||||
right = std::max(right, FrameGeometry.left());
|
||||
bottom = std::max(bottom, FrameGeometry.top());
|
||||
ContentGeometry = Rect::ltrb(left, top, right, bottom);
|
||||
|
||||
OnGeometryChanged();
|
||||
}
|
||||
|
||||
|
|
@ -709,7 +825,133 @@ void Widget::OnWindowDpiScaleChanged()
|
|||
{
|
||||
}
|
||||
|
||||
double Widget::GetDpiScale() const
|
||||
{
|
||||
Widget* w = Window();
|
||||
return w ? w->DispWindow->GetDpiScale() : 1.0;
|
||||
}
|
||||
|
||||
double Widget::GridFitPoint(double p) const
|
||||
{
|
||||
double dpiscale = GetDpiScale();
|
||||
return std::round(p * dpiscale) / dpiscale;
|
||||
}
|
||||
|
||||
double Widget::GridFitSize(double s) const
|
||||
{
|
||||
if (s <= 0.0)
|
||||
return 0.0;
|
||||
double dpiscale = GetDpiScale();
|
||||
return std::max(std::floor(s * dpiscale + 0.25), 1.0) / dpiscale;
|
||||
}
|
||||
|
||||
Size Widget::GetScreenSize()
|
||||
{
|
||||
return DisplayWindow::GetScreenSize();
|
||||
}
|
||||
|
||||
void* Widget::GetNativeHandle()
|
||||
{
|
||||
Widget* w = Window();
|
||||
return w ? w->DispWindow->GetNativeHandle() : nullptr;
|
||||
}
|
||||
|
||||
int Widget::GetNativePixelWidth()
|
||||
{
|
||||
Widget* w = Window();
|
||||
return w ? w->DispWindow->GetPixelWidth() : 0;
|
||||
}
|
||||
|
||||
int Widget::GetNativePixelHeight()
|
||||
{
|
||||
Widget* w = Window();
|
||||
return w ? w->DispWindow->GetPixelHeight() : 0;
|
||||
}
|
||||
|
||||
void Widget::SetStyleClass(const std::string& themeClass)
|
||||
{
|
||||
if (StyleClass != themeClass)
|
||||
{
|
||||
StyleClass = themeClass;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::SetStyleState(const std::string& state)
|
||||
{
|
||||
if (StyleState != state)
|
||||
{
|
||||
StyleState = state;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::SetStyleBool(const std::string& propertyName, bool value)
|
||||
{
|
||||
StyleProperties[propertyName] = value;
|
||||
}
|
||||
|
||||
void Widget::SetStyleInt(const std::string& propertyName, int value)
|
||||
{
|
||||
StyleProperties[propertyName] = value;
|
||||
}
|
||||
|
||||
void Widget::SetStyleDouble(const std::string& propertyName, double value)
|
||||
{
|
||||
StyleProperties[propertyName] = value;
|
||||
}
|
||||
|
||||
void Widget::SetStyleString(const std::string& propertyName, const std::string& value)
|
||||
{
|
||||
StyleProperties[propertyName] = value;
|
||||
}
|
||||
|
||||
void Widget::SetStyleColor(const std::string& propertyName, const Colorf& value)
|
||||
{
|
||||
StyleProperties[propertyName] = value;
|
||||
}
|
||||
|
||||
bool Widget::GetStyleBool(const std::string& propertyName) const
|
||||
{
|
||||
auto it = StyleProperties.find(propertyName);
|
||||
if (it != StyleProperties.end())
|
||||
return std::get<bool>(it->second);
|
||||
WidgetStyle* style = WidgetTheme::GetTheme()->GetStyle(StyleClass);
|
||||
return style ? style->GetBool(StyleState, propertyName) : false;
|
||||
}
|
||||
|
||||
int Widget::GetStyleInt(const std::string& propertyName) const
|
||||
{
|
||||
auto it = StyleProperties.find(propertyName);
|
||||
if (it != StyleProperties.end())
|
||||
return std::get<int>(it->second);
|
||||
WidgetStyle* style = WidgetTheme::GetTheme()->GetStyle(StyleClass);
|
||||
return style ? style->GetInt(StyleState, propertyName) : 0;
|
||||
}
|
||||
|
||||
double Widget::GetStyleDouble(const std::string& propertyName) const
|
||||
{
|
||||
auto it = StyleProperties.find(propertyName);
|
||||
if (it != StyleProperties.end())
|
||||
return std::get<double>(it->second);
|
||||
WidgetStyle* style = WidgetTheme::GetTheme()->GetStyle(StyleClass);
|
||||
return style ? style->GetDouble(StyleState, propertyName) : 0.0;
|
||||
}
|
||||
|
||||
std::string Widget::GetStyleString(const std::string& propertyName) const
|
||||
{
|
||||
auto it = StyleProperties.find(propertyName);
|
||||
if (it != StyleProperties.end())
|
||||
return std::get<std::string>(it->second);
|
||||
WidgetStyle* style = WidgetTheme::GetTheme()->GetStyle(StyleClass);
|
||||
return style ? style->GetString(StyleState, propertyName) : std::string();
|
||||
}
|
||||
|
||||
Colorf Widget::GetStyleColor(const std::string& propertyName) const
|
||||
{
|
||||
auto it = StyleProperties.find(propertyName);
|
||||
if (it != StyleProperties.end())
|
||||
return std::get<Colorf>(it->second);
|
||||
WidgetStyle* style = WidgetTheme::GetTheme()->GetStyle(StyleClass);
|
||||
return style ? style->GetColor(StyleState, propertyName) : Colorf::transparent();
|
||||
}
|
||||
|
|
|
|||
12
libraries/ZWidget/src/systemdialogs/open_file_dialog.cpp
Normal file
12
libraries/ZWidget/src/systemdialogs/open_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#include "systemdialogs/open_file_dialog.h"
|
||||
#include "window/window.h"
|
||||
#include "core/widget.h"
|
||||
|
||||
std::unique_ptr<OpenFileDialog> OpenFileDialog::Create(Widget* owner)
|
||||
{
|
||||
DisplayWindow* windowOwner = nullptr;
|
||||
if (owner && owner->Window())
|
||||
windowOwner = owner->Window()->DispWindow.get();
|
||||
return DisplayBackend::Get()->CreateOpenFileDialog(windowOwner);
|
||||
}
|
||||
12
libraries/ZWidget/src/systemdialogs/open_folder_dialog.cpp
Normal file
12
libraries/ZWidget/src/systemdialogs/open_folder_dialog.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#include "systemdialogs/open_folder_dialog.h"
|
||||
#include "window/window.h"
|
||||
#include "core/widget.h"
|
||||
|
||||
std::unique_ptr<OpenFolderDialog> OpenFolderDialog::Create(Widget* owner)
|
||||
{
|
||||
DisplayWindow* windowOwner = nullptr;
|
||||
if (owner && owner->Window())
|
||||
windowOwner = owner->Window()->DispWindow.get();
|
||||
return DisplayBackend::Get()->CreateOpenFolderDialog(windowOwner);
|
||||
}
|
||||
12
libraries/ZWidget/src/systemdialogs/save_file_dialog.cpp
Normal file
12
libraries/ZWidget/src/systemdialogs/save_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#include "systemdialogs/save_file_dialog.h"
|
||||
#include "window/window.h"
|
||||
#include "core/widget.h"
|
||||
|
||||
std::unique_ptr<SaveFileDialog> SaveFileDialog::Create(Widget* owner)
|
||||
{
|
||||
DisplayWindow* windowOwner = nullptr;
|
||||
if (owner && owner->Window())
|
||||
windowOwner = owner->Window()->DispWindow.get();
|
||||
return DisplayBackend::Get()->CreateSaveFileDialog(windowOwner);
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
CheckboxLabel::CheckboxLabel(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetStyleClass("checkbox-label");
|
||||
}
|
||||
|
||||
void CheckboxLabel::SetText(const std::string& value)
|
||||
|
|
@ -40,30 +41,37 @@ double CheckboxLabel::GetPreferredHeight() const
|
|||
|
||||
void CheckboxLabel::OnPaint(Canvas* canvas)
|
||||
{
|
||||
// To do: add and use GetStyleImage for the checkbox
|
||||
|
||||
double center = GridFitPoint(GetHeight() * 0.5);
|
||||
double borderwidth = GridFitSize(1.0);
|
||||
double outerboxsize = GridFitSize(10.0);
|
||||
double innerboxsize = outerboxsize - 2.0 * borderwidth;
|
||||
double checkedsize = innerboxsize - 2.0 * borderwidth;
|
||||
|
||||
if (checked)
|
||||
{
|
||||
canvas->fillRect(Rect::xywh(0.0, GetHeight() * 0.5 - 6.0, 10.0, 10.0), Colorf::fromRgba8(100, 100, 100));
|
||||
canvas->fillRect(Rect::xywh(1.0, GetHeight() * 0.5 - 5.0, 8.0, 8.0), Colorf::fromRgba8(51, 51, 51));
|
||||
canvas->fillRect(Rect::xywh(2.0, GetHeight() * 0.5 - 4.0, 6.0, 6.0), Colorf::fromRgba8(226, 223, 219));
|
||||
canvas->fillRect(Rect::xywh(0.0, center - 6.0 * borderwidth, outerboxsize, outerboxsize), GetStyleColor("checked-outer-border-color"));
|
||||
canvas->fillRect(Rect::xywh(1.0 * borderwidth, center - 5.0 * borderwidth, innerboxsize, innerboxsize), GetStyleColor("checked-inner-border-color"));
|
||||
canvas->fillRect(Rect::xywh(2.0 * borderwidth, center - 4.0 * borderwidth, checkedsize, checkedsize), GetStyleColor("checked-color"));
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas->fillRect(Rect::xywh(0.0, GetHeight() * 0.5 - 6.0, 10.0, 10.0), Colorf::fromRgba8(99, 99, 99));
|
||||
canvas->fillRect(Rect::xywh(1.0, GetHeight() * 0.5 - 5.0, 8.0, 8.0), Colorf::fromRgba8(51, 51, 51));
|
||||
canvas->fillRect(Rect::xywh(0.0, center - 6.0 * borderwidth, outerboxsize, outerboxsize), GetStyleColor("unchecked-outer-border-color"));
|
||||
canvas->fillRect(Rect::xywh(1.0 * borderwidth, center - 5.0 * borderwidth, innerboxsize, innerboxsize), GetStyleColor("unchecked-inner-border-color"));
|
||||
}
|
||||
|
||||
canvas->drawText(Point(14.0, GetHeight() - 5.0), Colorf::fromRgba8(255, 255, 255), text);
|
||||
canvas->drawText(Point(14.0, GetHeight() - 5.0), GetStyleColor("color"), text);
|
||||
}
|
||||
|
||||
bool CheckboxLabel::OnMouseDown(const Point& pos, int key)
|
||||
bool CheckboxLabel::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
mouseDownActive = true;
|
||||
SetFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckboxLabel::OnMouseUp(const Point& pos, int key)
|
||||
bool CheckboxLabel::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
if (mouseDownActive)
|
||||
{
|
||||
|
|
@ -78,9 +86,9 @@ void CheckboxLabel::OnMouseLeave()
|
|||
mouseDownActive = false;
|
||||
}
|
||||
|
||||
void CheckboxLabel::OnKeyUp(EInputKey key)
|
||||
void CheckboxLabel::OnKeyUp(InputKey key)
|
||||
{
|
||||
if (key == IK_Space)
|
||||
if (key == InputKey::Space)
|
||||
Toggle();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,14 @@ ImageBox::ImageBox(Widget* parent) : Widget(parent)
|
|||
{
|
||||
}
|
||||
|
||||
double ImageBox::GetPreferredWidth() const
|
||||
{
|
||||
if (image)
|
||||
return (double)image->GetWidth();
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double ImageBox::GetPreferredHeight() const
|
||||
{
|
||||
if (image)
|
||||
|
|
@ -22,10 +30,44 @@ void ImageBox::SetImage(std::shared_ptr<Image> newImage)
|
|||
}
|
||||
}
|
||||
|
||||
void ImageBox::SetImageMode(ImageBoxMode newMode)
|
||||
{
|
||||
if (mode != newMode)
|
||||
{
|
||||
mode = newMode;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageBox::OnPaint(Canvas* canvas)
|
||||
{
|
||||
if (image)
|
||||
{
|
||||
canvas->drawImage(image, Point((GetWidth() - (double)image->GetWidth()) * 0.5, (GetHeight() - (double)image->GetHeight()) * 0.5));
|
||||
if (mode == ImageBoxMode::Center)
|
||||
{
|
||||
canvas->drawImage(image, Point((GetWidth() - (double)image->GetWidth()) * 0.5, (GetHeight() - (double)image->GetHeight()) * 0.5));
|
||||
}
|
||||
else if (mode == ImageBoxMode::Contain)
|
||||
{
|
||||
double bw = GetWidth();
|
||||
double bh = GetHeight();
|
||||
double iw = image->GetWidth();
|
||||
double ih = image->GetHeight();
|
||||
double xscale = bw / iw;
|
||||
double yscale = bh / ih;
|
||||
double scale = std::min(xscale, yscale);
|
||||
canvas->drawImage(image, Rect::xywh((bw - iw * scale) * 0.5, (bh - ih * scale) * 0.5, iw * scale, ih * scale));
|
||||
}
|
||||
else if (mode == ImageBoxMode::Cover)
|
||||
{
|
||||
double bw = GetWidth();
|
||||
double bh = GetHeight();
|
||||
double iw = image->GetWidth();
|
||||
double ih = image->GetHeight();
|
||||
double xscale = bw / iw;
|
||||
double yscale = bh / ih;
|
||||
double scale = std::max(xscale, yscale);
|
||||
canvas->drawImage(image, Rect::xywh((bw - iw * scale) * 0.5, (bh - ih * scale) * 0.5, iw * scale, ih * scale));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "widgets/lineedit/lineedit.h"
|
||||
#include "core/utf8reader.h"
|
||||
#include "core/colorf.h"
|
||||
#include <algorithm>
|
||||
|
||||
LineEdit::LineEdit(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetNoncontentSizes(5.0, 3.0, 5.0, 3.0);
|
||||
SetStyleClass("lineedit");
|
||||
|
||||
timer = new Timer(this);
|
||||
timer->FuncExpired = [=]() { OnTimerExpired(); };
|
||||
|
|
@ -18,8 +19,6 @@ LineEdit::LineEdit(Widget* parent) : Widget(parent)
|
|||
|
||||
LineEdit::~LineEdit()
|
||||
{
|
||||
delete timer;
|
||||
delete scroll_timer;
|
||||
}
|
||||
|
||||
bool LineEdit::IsReadOnly() const
|
||||
|
|
@ -303,13 +302,13 @@ void LineEdit::OnMouseMove(const Point& pos)
|
|||
}
|
||||
}
|
||||
|
||||
bool LineEdit::OnMouseDown(const Point& pos, int key)
|
||||
bool LineEdit::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
if (key == IK_LeftMouse)
|
||||
if (key == InputKey::LeftMouse)
|
||||
{
|
||||
if (HasFocus())
|
||||
{
|
||||
CaptureMouse();
|
||||
SetPointerCapture();
|
||||
mouse_selecting = true;
|
||||
cursor_pos = GetCharacterIndex(pos.x);
|
||||
SetTextSelection(cursor_pos, 0);
|
||||
|
|
@ -323,25 +322,25 @@ bool LineEdit::OnMouseDown(const Point& pos, int key)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool LineEdit::OnMouseDoubleclick(const Point& pos, int key)
|
||||
bool LineEdit::OnMouseDoubleclick(const Point& pos, InputKey key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LineEdit::OnMouseUp(const Point& pos, int key)
|
||||
bool LineEdit::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
if (mouse_selecting && key == IK_LeftMouse)
|
||||
if (mouse_selecting && key == InputKey::LeftMouse)
|
||||
{
|
||||
if (ignore_mouse_events) // This prevents text selection from changing from what was set when focus was gained.
|
||||
{
|
||||
ReleaseMouseCapture();
|
||||
ReleasePointerCapture();
|
||||
ignore_mouse_events = false;
|
||||
mouse_selecting = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
scroll_timer->Stop();
|
||||
ReleaseMouseCapture();
|
||||
ReleasePointerCapture();
|
||||
mouse_selecting = false;
|
||||
int sel_end = GetCharacterIndex(pos.x);
|
||||
SetSelectionLength(sel_end - selection_start);
|
||||
|
|
@ -414,12 +413,12 @@ void LineEdit::OnKeyChar(std::string chars)
|
|||
}
|
||||
}
|
||||
|
||||
void LineEdit::OnKeyDown(EInputKey key)
|
||||
void LineEdit::OnKeyDown(InputKey key)
|
||||
{
|
||||
if (FuncIgnoreKeyDown && FuncIgnoreKeyDown(key))
|
||||
return;
|
||||
|
||||
if (key == IK_Enter)
|
||||
if (key == InputKey::Enter)
|
||||
{
|
||||
if (FuncEnterPressed)
|
||||
FuncEnterPressed();
|
||||
|
|
@ -432,12 +431,12 @@ void LineEdit::OnKeyDown(EInputKey key)
|
|||
timer->Start(500); // don't blink cursor when moving or typing.
|
||||
}
|
||||
|
||||
if (key == IK_Enter || key == IK_Escape || key == IK_Tab)
|
||||
if (key == InputKey::Enter || key == InputKey::Escape || key == InputKey::Tab)
|
||||
{
|
||||
// Do not consume these.
|
||||
return;
|
||||
}
|
||||
else if (key == IK_A && GetKeyState(IK_Ctrl))
|
||||
else if (key == InputKey::A && GetKeyState(InputKey::Ctrl))
|
||||
{
|
||||
// select all
|
||||
SetTextSelection(0, (int)text.size());
|
||||
|
|
@ -445,7 +444,7 @@ void LineEdit::OnKeyDown(EInputKey key)
|
|||
UpdateTextClipping();
|
||||
Update();
|
||||
}
|
||||
else if (key == IK_C && GetKeyState(IK_Ctrl))
|
||||
else if (key == InputKey::C && GetKeyState(InputKey::Ctrl))
|
||||
{
|
||||
if (!password_mode) // Do not allow copying the password to clipboard
|
||||
{
|
||||
|
|
@ -458,54 +457,54 @@ void LineEdit::OnKeyDown(EInputKey key)
|
|||
// Do not consume messages on read only component (only allow CTRL-A and CTRL-C)
|
||||
return;
|
||||
}
|
||||
else if (key == IK_Left)
|
||||
else if (key == InputKey::Left)
|
||||
{
|
||||
Move(-1, GetKeyState(IK_Ctrl), GetKeyState(IK_Shift));
|
||||
Move(-1, GetKeyState(InputKey::Ctrl), GetKeyState(InputKey::Shift));
|
||||
}
|
||||
else if (key == IK_Right)
|
||||
else if (key == InputKey::Right)
|
||||
{
|
||||
Move(1, GetKeyState(IK_Ctrl), GetKeyState(IK_Shift));
|
||||
Move(1, GetKeyState(InputKey::Ctrl), GetKeyState(InputKey::Shift));
|
||||
}
|
||||
else if (key == IK_Backspace)
|
||||
else if (key == InputKey::Backspace)
|
||||
{
|
||||
Backspace();
|
||||
UpdateTextClipping();
|
||||
}
|
||||
else if (key == IK_Delete)
|
||||
else if (key == InputKey::Delete)
|
||||
{
|
||||
Del();
|
||||
UpdateTextClipping();
|
||||
}
|
||||
else if (key == IK_Home)
|
||||
else if (key == InputKey::Home)
|
||||
{
|
||||
SetSelectionStart(cursor_pos);
|
||||
cursor_pos = 0;
|
||||
if (GetKeyState(IK_Shift))
|
||||
if (GetKeyState(InputKey::Shift))
|
||||
SetSelectionLength(-selection_start);
|
||||
else
|
||||
SetTextSelection(0, 0);
|
||||
UpdateTextClipping();
|
||||
Update();
|
||||
}
|
||||
else if (key == IK_End)
|
||||
else if (key == InputKey::End)
|
||||
{
|
||||
SetSelectionStart(cursor_pos);
|
||||
cursor_pos = (int)text.size();
|
||||
if (GetKeyState(IK_Shift))
|
||||
if (GetKeyState(InputKey::Shift))
|
||||
SetSelectionLength((int)text.size() - selection_start);
|
||||
else
|
||||
SetTextSelection(0, 0);
|
||||
UpdateTextClipping();
|
||||
Update();
|
||||
}
|
||||
else if (key == IK_X && GetKeyState(IK_Ctrl))
|
||||
else if (key == InputKey::X && GetKeyState(InputKey::Ctrl))
|
||||
{
|
||||
std::string str = GetSelection();
|
||||
DeleteSelectedText();
|
||||
SetClipboardText(str);
|
||||
UpdateTextClipping();
|
||||
}
|
||||
else if (key == IK_V && GetKeyState(IK_Ctrl))
|
||||
else if (key == InputKey::V && GetKeyState(InputKey::Ctrl))
|
||||
{
|
||||
std::string str = GetClipboardText();
|
||||
std::string::const_iterator end_str = std::remove(str.begin(), str.end(), '\n');
|
||||
|
|
@ -576,7 +575,7 @@ void LineEdit::OnKeyDown(EInputKey key)
|
|||
|
||||
UpdateTextClipping();
|
||||
}
|
||||
else if (GetKeyState(IK_Ctrl) && key == IK_Z)
|
||||
else if (GetKeyState(InputKey::Ctrl) && key == InputKey::Z)
|
||||
{
|
||||
if (!readonly)
|
||||
{
|
||||
|
|
@ -585,7 +584,7 @@ void LineEdit::OnKeyDown(EInputKey key)
|
|||
SetText(tmp);
|
||||
}
|
||||
}
|
||||
else if (key == IK_Shift)
|
||||
else if (key == InputKey::Shift)
|
||||
{
|
||||
if (selection_start == -1)
|
||||
SetTextSelection(cursor_pos, 0);
|
||||
|
|
@ -595,7 +594,7 @@ void LineEdit::OnKeyDown(EInputKey key)
|
|||
FuncAfterEditChanged();
|
||||
}
|
||||
|
||||
void LineEdit::OnKeyUp(EInputKey key)
|
||||
void LineEdit::OnKeyUp(InputKey key)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -817,6 +816,8 @@ int LineEdit::GetCharacterIndex(double mouse_x)
|
|||
void LineEdit::UpdateTextClipping()
|
||||
{
|
||||
Canvas* canvas = GetCanvas();
|
||||
if (!canvas)
|
||||
return;
|
||||
|
||||
Size text_size = GetVisualTextSize(canvas, clip_start_offset, (int)text.size() - clip_start_offset);
|
||||
|
||||
|
|
@ -1067,18 +1068,6 @@ std::string LineEdit::GetVisibleTextAfterSelection()
|
|||
}
|
||||
}
|
||||
|
||||
void LineEdit::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
double w = GetFrameGeometry().width;
|
||||
double h = GetFrameGeometry().height;
|
||||
Colorf bordercolor = Colorf::fromRgba8(100, 100, 100);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(38, 38, 38));
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, h - 1.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(w - 1.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
}
|
||||
|
||||
void LineEdit::OnPaint(Canvas* canvas)
|
||||
{
|
||||
std::string txt_before = GetVisibleTextBeforeSelection();
|
||||
|
|
@ -1101,21 +1090,21 @@ void LineEdit::OnPaint(Canvas* canvas)
|
|||
{
|
||||
// Draw selection box.
|
||||
Rect selection_rect = GetSelectionRect();
|
||||
canvas->fillRect(selection_rect, HasFocus() ? Colorf::fromRgba8(100, 100, 100) : Colorf::fromRgba8(68, 68, 68));
|
||||
canvas->fillRect(selection_rect, HasFocus() ? GetStyleColor("selection-color") : GetStyleColor("no-focus-selection-color"));
|
||||
}
|
||||
|
||||
// Draw text before selection
|
||||
if (!txt_before.empty())
|
||||
{
|
||||
canvas->drawText(Point(0.0, canvas->verticalTextAlign().baseline), Colorf::fromRgba8(255, 255, 255), txt_before);
|
||||
canvas->drawText(Point(0.0, canvas->verticalTextAlign().baseline), GetStyleColor("color"), txt_before);
|
||||
}
|
||||
if (!txt_selected.empty())
|
||||
{
|
||||
canvas->drawText(Point(size_before.width, canvas->verticalTextAlign().baseline), Colorf::fromRgba8(255, 255, 255), txt_selected);
|
||||
canvas->drawText(Point(size_before.width, canvas->verticalTextAlign().baseline), GetStyleColor("color"), txt_selected);
|
||||
}
|
||||
if (!txt_after.empty())
|
||||
{
|
||||
canvas->drawText(Point(size_before.width + size_selected.width, canvas->verticalTextAlign().baseline), Colorf::fromRgba8(255, 255, 255), txt_after);
|
||||
canvas->drawText(Point(size_before.width + size_selected.width, canvas->verticalTextAlign().baseline), GetStyleColor("color"), txt_after);
|
||||
}
|
||||
|
||||
// draw cursor
|
||||
|
|
@ -1124,7 +1113,7 @@ void LineEdit::OnPaint(Canvas* canvas)
|
|||
if (cursor_blink_visible)
|
||||
{
|
||||
Rect cursor_rect = GetCursorRect();
|
||||
canvas->fillRect(cursor_rect, Colorf::fromRgba8(255, 255, 255));
|
||||
canvas->fillRect(cursor_rect, GetStyleColor("color"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
ListView::ListView(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetNoncontentSizes(10.0, 10.0, 3.0, 10.0);
|
||||
SetStyleClass("listview");
|
||||
|
||||
scrollbar = new Scrollbar(this);
|
||||
scrollbar->FuncScroll = [=]() { OnScrollbarScroll(); };
|
||||
|
|
@ -28,7 +28,7 @@ void ListView::SetColumnWidths(const std::vector<double>& widths)
|
|||
while (column.size() > newWidth)
|
||||
{
|
||||
updated = true;
|
||||
column.erase(column.end());
|
||||
column.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +146,9 @@ void ListView::OnPaint(Canvas* canvas)
|
|||
double w = GetWidth() - scrollbar->GetPreferredWidth() - 2.0;
|
||||
double h = 20.0;
|
||||
|
||||
Colorf textColor = GetStyleColor("color");
|
||||
Colorf selectionColor = GetStyleColor("selection-color");
|
||||
|
||||
int index = 0;
|
||||
for (const std::vector<std::string>& item : items)
|
||||
{
|
||||
|
|
@ -154,12 +157,12 @@ void ListView::OnPaint(Canvas* canvas)
|
|||
{
|
||||
if (index == selectedItem)
|
||||
{
|
||||
canvas->fillRect(Rect::xywh(x - 2.0, itemY, w, h), Colorf::fromRgba8(100, 100, 100));
|
||||
canvas->fillRect(Rect::xywh(x - 2.0, itemY, w, h), selectionColor);
|
||||
}
|
||||
double cx = x;
|
||||
for (size_t entry = 0u; entry < item.size(); ++entry)
|
||||
{
|
||||
canvas->drawText(Point(cx, y + 15.0), Colorf::fromRgba8(255, 255, 255), item[entry]);
|
||||
canvas->drawText(Point(cx, y + 15.0), textColor, item[entry]);
|
||||
cx += columnwidths[entry];
|
||||
}
|
||||
}
|
||||
|
|
@ -168,23 +171,11 @@ void ListView::OnPaint(Canvas* canvas)
|
|||
}
|
||||
}
|
||||
|
||||
void ListView::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
double w = GetFrameGeometry().width;
|
||||
double h = GetFrameGeometry().height;
|
||||
Colorf bordercolor = Colorf::fromRgba8(100, 100, 100);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(38, 38, 38));
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, h - 1.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(w - 1.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
}
|
||||
|
||||
bool ListView::OnMouseDown(const Point& pos, int key)
|
||||
bool ListView::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
SetFocus();
|
||||
|
||||
if (key == IK_LeftMouse)
|
||||
if (key == InputKey::LeftMouse)
|
||||
{
|
||||
int index = (int)((pos.y - 5.0 + scrollbar->GetPosition()) / 20.0);
|
||||
if (index >= 0 && (size_t)index < items.size())
|
||||
|
|
@ -196,31 +187,31 @@ bool ListView::OnMouseDown(const Point& pos, int key)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ListView::OnMouseDoubleclick(const Point& pos, int key)
|
||||
bool ListView::OnMouseDoubleclick(const Point& pos, InputKey key)
|
||||
{
|
||||
if (key == IK_LeftMouse)
|
||||
if (key == InputKey::LeftMouse)
|
||||
{
|
||||
Activate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ListView::OnMouseWheel(const Point& pos, EInputKey key)
|
||||
bool ListView::OnMouseWheel(const Point& pos, InputKey key)
|
||||
{
|
||||
if (key == IK_MouseWheelUp)
|
||||
if (key == InputKey::MouseWheelUp)
|
||||
{
|
||||
scrollbar->SetPosition(std::max(scrollbar->GetPosition() - 20.0, 0.0));
|
||||
}
|
||||
else if (key == IK_MouseWheelDown)
|
||||
else if (key == InputKey::MouseWheelDown)
|
||||
{
|
||||
scrollbar->SetPosition(std::min(scrollbar->GetPosition() + 20.0, scrollbar->GetMax()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ListView::OnKeyDown(EInputKey key)
|
||||
void ListView::OnKeyDown(InputKey key)
|
||||
{
|
||||
if (key == IK_Down)
|
||||
if (key == InputKey::Down)
|
||||
{
|
||||
if (selectedItem + 1 < (int)items.size())
|
||||
{
|
||||
|
|
@ -228,7 +219,7 @@ void ListView::OnKeyDown(EInputKey key)
|
|||
}
|
||||
ScrollToItem(selectedItem);
|
||||
}
|
||||
else if (key == IK_Up)
|
||||
else if (key == InputKey::Up)
|
||||
{
|
||||
if (selectedItem > 0)
|
||||
{
|
||||
|
|
@ -236,7 +227,7 @@ void ListView::OnKeyDown(EInputKey key)
|
|||
}
|
||||
ScrollToItem(selectedItem);
|
||||
}
|
||||
else if (key == IK_Enter)
|
||||
else if (key == InputKey::Enter)
|
||||
{
|
||||
Activate();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,13 @@
|
|||
#include "widgets/toolbar/toolbar.h"
|
||||
#include "widgets/statusbar/statusbar.h"
|
||||
|
||||
MainWindow::MainWindow() : Widget(nullptr, WidgetType::Window)
|
||||
MainWindow::MainWindow(RenderAPI api) : Widget(nullptr, WidgetType::Window, api)
|
||||
{
|
||||
MenubarWidget = new Menubar(this);
|
||||
// ToolbarWidget = new Toolbar(this);
|
||||
TopToolbarWidget = new Toolbar(this);
|
||||
TopToolbarWidget->SetDirection(ToolbarDirection::Horizontal);
|
||||
LeftToolbarWidget = new Toolbar(this);
|
||||
LeftToolbarWidget->SetDirection(ToolbarDirection::Vertical);
|
||||
StatusbarWidget = new Statusbar(this);
|
||||
}
|
||||
|
||||
|
|
@ -32,9 +35,10 @@ void MainWindow::OnGeometryChanged()
|
|||
Size s = GetSize();
|
||||
|
||||
MenubarWidget->SetFrameGeometry(0.0, 0.0, s.width, 32.0);
|
||||
// ToolbarWidget->SetFrameGeometry(0.0, 32.0, s.width, 36.0);
|
||||
TopToolbarWidget->SetFrameGeometry(0.0, 32.0, s.width, 32.0);
|
||||
LeftToolbarWidget->SetFrameGeometry(0.0, 64.0, 32.0, s.height - 64.0);
|
||||
StatusbarWidget->SetFrameGeometry(0.0, s.height - 32.0, s.width, 32.0);
|
||||
|
||||
if (CentralWidget)
|
||||
CentralWidget->SetFrameGeometry(0.0, 32.0, s.width, s.height - 32.0 - 32.0);
|
||||
CentralWidget->SetFrameGeometry(32.0, 64.0, s.width - 32.0, s.height - 64.0 - 32.0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,387 @@
|
|||
|
||||
Menubar::Menubar(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetStyleClass("menubar");
|
||||
}
|
||||
|
||||
Menubar::~Menubar()
|
||||
{
|
||||
}
|
||||
|
||||
void Menubar::OnPaint(Canvas* canvas)
|
||||
MenubarItem* Menubar::AddItem(std::string text, std::function<void(Menu* menu)> onOpen, bool alignRight)
|
||||
{
|
||||
canvas->drawText(Point(16.0, 21.0), Colorf::fromRgba8(0, 0, 0), "File Edit View Tools Window Help");
|
||||
auto item = new MenubarItem(this, text, alignRight);
|
||||
item->SetOpenCallback(std::move(onOpen));
|
||||
menuItems.push_back(item);
|
||||
OnGeometryChanged();
|
||||
return item;
|
||||
}
|
||||
|
||||
void Menubar::ShowMenu(MenubarItem* item)
|
||||
{
|
||||
int index = GetItemIndex(item);
|
||||
if (index == currentMenubarItem)
|
||||
return;
|
||||
|
||||
CloseMenu();
|
||||
SetFocus();
|
||||
SetModalCapture();
|
||||
currentMenubarItem = index;
|
||||
if (currentMenubarItem != -1)
|
||||
menuItems[currentMenubarItem]->SetStyleState("hover");
|
||||
modalMode = true;
|
||||
if (item->GetOpenCallback())
|
||||
{
|
||||
openMenu = new Menu(this);
|
||||
openMenu->onCloseMenu = [=]() { CloseMenu(); };
|
||||
item->GetOpenCallback()(openMenu);
|
||||
if (item->AlignRight)
|
||||
openMenu->SetRightPosition(item->MapToGlobal(Point(item->GetWidth(), item->GetHeight())));
|
||||
else
|
||||
openMenu->SetLeftPosition(item->MapToGlobal(Point(0.0, item->GetHeight())));
|
||||
openMenu->Show();
|
||||
}
|
||||
}
|
||||
|
||||
void Menubar::CloseMenu()
|
||||
{
|
||||
if (currentMenubarItem != -1)
|
||||
menuItems[currentMenubarItem]->SetStyleState("");
|
||||
currentMenubarItem = -1;
|
||||
delete openMenu;
|
||||
openMenu = nullptr;
|
||||
ReleaseModalCapture();
|
||||
modalMode = false;
|
||||
}
|
||||
|
||||
void Menubar::OnGeometryChanged()
|
||||
{
|
||||
double w = GetWidth();
|
||||
double h = GetHeight();
|
||||
double left = 0.0;
|
||||
double right = w;
|
||||
for (MenubarItem* item : menuItems)
|
||||
{
|
||||
double itemwidth = item->GetPreferredWidth();
|
||||
itemwidth += 16.0;
|
||||
if (!item->AlignRight)
|
||||
{
|
||||
item->SetFrameGeometry(left, 0.0, itemwidth, h);
|
||||
left += itemwidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
right -= itemwidth;
|
||||
item->SetFrameGeometry(right, 0.0, itemwidth, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenubarItem* Menubar::GetMenubarItemAt(const Point& pos)
|
||||
{
|
||||
Widget* widget = ChildAt(pos);
|
||||
for (MenubarItem* item : menuItems)
|
||||
{
|
||||
if (widget == item)
|
||||
return item;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Menubar::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
if (!modalMode)
|
||||
return Widget::OnMouseDown(pos, key);
|
||||
|
||||
MenubarItem* item = GetMenubarItemAt(pos);
|
||||
if (item)
|
||||
ShowMenu(item);
|
||||
else
|
||||
CloseMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Menubar::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
if (!modalMode)
|
||||
return Widget::OnMouseUp(pos, key);
|
||||
|
||||
MenubarItem* item = GetMenubarItemAt(pos);
|
||||
if (!item)
|
||||
CloseMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Menubar::OnMouseMove(const Point& pos)
|
||||
{
|
||||
if (!modalMode)
|
||||
return Widget::OnMouseMove(pos);
|
||||
|
||||
MenubarItem* item = GetMenubarItemAt(pos);
|
||||
if (item)
|
||||
ShowMenu(item);
|
||||
}
|
||||
|
||||
int Menubar::GetItemIndex(MenubarItem* item)
|
||||
{
|
||||
int i = 0;
|
||||
for (MenubarItem* cur : menuItems)
|
||||
{
|
||||
if (cur == item)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Menubar::OnKeyDown(InputKey key)
|
||||
{
|
||||
if (!modalMode)
|
||||
return Widget::OnKeyDown(key);
|
||||
|
||||
if (key == InputKey::Left)
|
||||
{
|
||||
if (!menuItems.empty())
|
||||
{
|
||||
int index = currentMenubarItem - 1;
|
||||
if (index < 0)
|
||||
index = (int)menuItems.size() - 1;
|
||||
ShowMenu(menuItems[index]);
|
||||
}
|
||||
}
|
||||
else if (key == InputKey::Right)
|
||||
{
|
||||
if (!menuItems.empty())
|
||||
{
|
||||
int index = currentMenubarItem + 1;
|
||||
if (index == (int)menuItems.size())
|
||||
index = 0;
|
||||
ShowMenu(menuItems[index]);
|
||||
}
|
||||
}
|
||||
else if (key == InputKey::Up)
|
||||
{
|
||||
if (openMenu)
|
||||
{
|
||||
// Keep trying until we don't find a separator
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (!openMenu->selectedItem || !openMenu->selectedItem->PrevSibling())
|
||||
{
|
||||
if (openMenu->LastChild())
|
||||
openMenu->SetSelected(static_cast<MenuItem*>(openMenu->LastChild()));
|
||||
}
|
||||
else
|
||||
{
|
||||
openMenu->SetSelected(static_cast<MenuItem*>(openMenu->selectedItem->PrevSibling()));
|
||||
}
|
||||
|
||||
if (openMenu->selectedItem && openMenu->selectedItem->GetStyleClass() != "menuitemseparator")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == InputKey::Down)
|
||||
{
|
||||
if (openMenu)
|
||||
{
|
||||
// Keep trying until we don't find a separator
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (!openMenu->selectedItem || !openMenu->selectedItem->NextSibling())
|
||||
{
|
||||
if (openMenu->FirstChild())
|
||||
openMenu->SetSelected(static_cast<MenuItem*>(openMenu->FirstChild()));
|
||||
}
|
||||
else
|
||||
{
|
||||
openMenu->SetSelected(static_cast<MenuItem*>(openMenu->selectedItem->NextSibling()));
|
||||
}
|
||||
|
||||
if (openMenu->selectedItem && openMenu->selectedItem->GetStyleClass() != "menuitemseparator")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == InputKey::Enter)
|
||||
{
|
||||
if (openMenu && openMenu->selectedItem)
|
||||
openMenu->selectedItem->Click();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MenubarItem::MenubarItem(Menubar* menubar, std::string text, bool alignRight) : Widget(menubar), menubar(menubar), text(text), AlignRight(alignRight)
|
||||
{
|
||||
SetStyleClass("menubaritem");
|
||||
}
|
||||
|
||||
bool MenubarItem::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
menubar->ShowMenu(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MenubarItem::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void MenubarItem::OnMouseMove(const Point& pos)
|
||||
{
|
||||
if (GetStyleState().empty())
|
||||
{
|
||||
SetStyleState("hover");
|
||||
}
|
||||
}
|
||||
|
||||
void MenubarItem::OnMouseLeave()
|
||||
{
|
||||
SetStyleState("");
|
||||
}
|
||||
|
||||
double MenubarItem::GetPreferredWidth() const
|
||||
{
|
||||
Canvas* canvas = GetCanvas();
|
||||
return canvas->measureText(text).width;
|
||||
}
|
||||
|
||||
void MenubarItem::OnPaint(Canvas* canvas)
|
||||
{
|
||||
double x = (GetWidth() - canvas->measureText(text).width) * 0.5;
|
||||
canvas->drawText(Point(x, 21.0), GetStyleColor("color"), text);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Menu::Menu(Widget* parent) : Widget(parent, WidgetType::Popup)
|
||||
{
|
||||
SetStyleClass("menu");
|
||||
}
|
||||
|
||||
void Menu::SetLeftPosition(const Point& pos)
|
||||
{
|
||||
SetFrameGeometry(Rect::xywh(pos.x, pos.y, GetPreferredWidth() + GetNoncontentLeft() + GetNoncontentRight(), GetPreferredHeight() + GetNoncontentTop() + GetNoncontentBottom()));
|
||||
}
|
||||
|
||||
void Menu::SetRightPosition(const Point& pos)
|
||||
{
|
||||
SetFrameGeometry(Rect::xywh(pos.x - GetWidth() - GetNoncontentLeft() - GetNoncontentRight(), pos.y, GetWidth() + GetNoncontentLeft() + GetNoncontentRight(), GetHeight() + GetNoncontentTop() + GetNoncontentBottom()));
|
||||
}
|
||||
|
||||
MenuItem* Menu::AddItem(std::shared_ptr<Image> icon, std::string text, std::function<void()> onClick)
|
||||
{
|
||||
auto item = new MenuItem(this, [this, onClick]() { if (onCloseMenu) onCloseMenu(); if (onClick) onClick(); });
|
||||
if (icon)
|
||||
item->icon->SetImage(icon);
|
||||
item->text->SetText(text);
|
||||
return item;
|
||||
}
|
||||
|
||||
MenuItemSeparator* Menu::AddSeparator()
|
||||
{
|
||||
auto sep = new MenuItemSeparator(this);
|
||||
return sep;
|
||||
}
|
||||
|
||||
double Menu::GetPreferredWidth() const
|
||||
{
|
||||
return GridFitSize(200.0);
|
||||
}
|
||||
|
||||
double Menu::GetPreferredHeight() const
|
||||
{
|
||||
double h = 0.0;
|
||||
for (Widget* item = FirstChild(); item != nullptr; item = item->NextSibling())
|
||||
{
|
||||
double itemheight = GridFitSize((item->GetStyleClass() == "menuitemseparator") ? 7.0 : 30.0);
|
||||
h += itemheight;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
void Menu::OnGeometryChanged()
|
||||
{
|
||||
double w = GetWidth();
|
||||
double y = 0.0;
|
||||
for (Widget* item = FirstChild(); item != nullptr; item = item->NextSibling())
|
||||
{
|
||||
double itemheight = GridFitSize((item->GetStyleClass() == "menuitemseparator") ? 7.0 : 30.0);
|
||||
item->SetFrameGeometry(Rect::xywh(0.0, y, w, itemheight));
|
||||
y += itemheight;
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::SetSelected(MenuItem* item)
|
||||
{
|
||||
if (selectedItem)
|
||||
{
|
||||
selectedItem->SetStyleState("");
|
||||
}
|
||||
selectedItem = item;
|
||||
if (selectedItem)
|
||||
{
|
||||
selectedItem->SetStyleState("hover");
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MenuItem::MenuItem(Menu* menu, std::function<void()> onClick) : Widget(menu), menu(menu), onClick(onClick)
|
||||
{
|
||||
SetStyleClass("menuitem");
|
||||
icon = new ImageBox(this);
|
||||
text = new TextLabel(this);
|
||||
}
|
||||
|
||||
void MenuItem::OnMouseMove(const Point& pos)
|
||||
{
|
||||
menu->SetSelected(this);
|
||||
}
|
||||
|
||||
bool MenuItem::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
Click();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MenuItem::Click()
|
||||
{
|
||||
if (onClick)
|
||||
{
|
||||
// We have to make a copy of the handler as it may delete 'this'
|
||||
auto handler = onClick;
|
||||
handler();
|
||||
}
|
||||
}
|
||||
|
||||
void MenuItem::OnMouseLeave()
|
||||
{
|
||||
menu->SetSelected(nullptr);
|
||||
}
|
||||
|
||||
void MenuItem::OnGeometryChanged()
|
||||
{
|
||||
double iconwidth = GridFitSize(icon->GetPreferredWidth());
|
||||
double iconheight = GridFitSize(icon->GetPreferredHeight());
|
||||
double w = GetWidth();
|
||||
double h = GetHeight();
|
||||
double textheight = 19.0;
|
||||
double x0 = GridFitPoint(5.0);
|
||||
double x1 = GridFitPoint(5.0 + iconwidth);
|
||||
icon->SetFrameGeometry(Rect::xywh(x0, GridFitPoint((h - iconheight) * 0.5), iconwidth, iconheight));
|
||||
text->SetFrameGeometry(Rect::xywh(x1, GridFitPoint((h - textheight) * 0.5), w - x1, textheight));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MenuItemSeparator::MenuItemSeparator(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetStyleClass("menuitemseparator");
|
||||
}
|
||||
|
||||
void MenuItemSeparator::OnPaint(Canvas* canvas)
|
||||
{
|
||||
canvas->fillRect(Rect::xywh(0.0, GridFitPoint(GetHeight() * 0.5), GetWidth(), GridFitSize(1.0)), Colorf::fromRgba8(75, 75, 75));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
PushButton::PushButton(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetNoncontentSizes(10.0, 5.0, 10.0, 5.0);
|
||||
SetStyleClass("pushbutton");
|
||||
}
|
||||
|
||||
void PushButton::SetText(const std::string& value)
|
||||
|
|
@ -25,52 +25,32 @@ double PushButton::GetPreferredHeight() const
|
|||
return 30.0;
|
||||
}
|
||||
|
||||
void PushButton::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
double w = GetFrameGeometry().width;
|
||||
double h = GetFrameGeometry().height;
|
||||
Colorf bordercolor = Colorf::fromRgba8(100, 100, 100);
|
||||
Colorf buttoncolor = Colorf::fromRgba8(68, 68, 68);
|
||||
if (buttonDown)
|
||||
buttoncolor = Colorf::fromRgba8(88, 88, 88);
|
||||
else if (hot)
|
||||
buttoncolor = Colorf::fromRgba8(78, 78, 78);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), buttoncolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, h - 1.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(w - 1.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
}
|
||||
|
||||
void PushButton::OnPaint(Canvas* canvas)
|
||||
{
|
||||
Rect box = canvas->measureText(text);
|
||||
canvas->drawText(Point((GetWidth() - box.width) * 0.5, GetHeight() - 5.0), Colorf::fromRgba8(255, 255, 255), text);
|
||||
canvas->drawText(Point((GetWidth() - box.width) * 0.5, GetHeight() - 5.0), GetStyleColor("color"), text);
|
||||
}
|
||||
|
||||
void PushButton::OnMouseMove(const Point& pos)
|
||||
{
|
||||
if (!hot)
|
||||
if (GetStyleState().empty())
|
||||
{
|
||||
hot = true;
|
||||
Update();
|
||||
SetStyleState("hover");
|
||||
}
|
||||
}
|
||||
|
||||
bool PushButton::OnMouseDown(const Point& pos, int key)
|
||||
bool PushButton::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
SetFocus();
|
||||
buttonDown = true;
|
||||
Update();
|
||||
SetStyleState("down");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PushButton::OnMouseUp(const Point& pos, int key)
|
||||
bool PushButton::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
if (buttonDown)
|
||||
if (GetStyleState() == "down")
|
||||
{
|
||||
buttonDown = false;
|
||||
hot = false;
|
||||
SetStyleState("");
|
||||
Repaint();
|
||||
Click();
|
||||
}
|
||||
|
|
@ -79,26 +59,23 @@ bool PushButton::OnMouseUp(const Point& pos, int key)
|
|||
|
||||
void PushButton::OnMouseLeave()
|
||||
{
|
||||
hot = false;
|
||||
buttonDown = false;
|
||||
Update();
|
||||
SetStyleState("");
|
||||
}
|
||||
|
||||
void PushButton::OnKeyDown(EInputKey key)
|
||||
void PushButton::OnKeyDown(InputKey key)
|
||||
{
|
||||
if (key == IK_Space || key == IK_Enter)
|
||||
if (key == InputKey::Space || key == InputKey::Enter)
|
||||
{
|
||||
buttonDown = true;
|
||||
SetStyleState("down");
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void PushButton::OnKeyUp(EInputKey key)
|
||||
void PushButton::OnKeyUp(InputKey key)
|
||||
{
|
||||
if (key == IK_Space || key == IK_Enter)
|
||||
if (key == InputKey::Space || key == InputKey::Enter)
|
||||
{
|
||||
buttonDown = false;
|
||||
hot = false;
|
||||
SetStyleState("");
|
||||
Repaint();
|
||||
Click();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
Scrollbar::Scrollbar(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetStyleClass("scrollbar");
|
||||
UpdatePartPositions();
|
||||
|
||||
mouse_down_timer = new Timer(this);
|
||||
|
|
@ -169,7 +170,7 @@ void Scrollbar::OnMouseMove(const Point& pos)
|
|||
Update();
|
||||
}
|
||||
|
||||
bool Scrollbar::OnMouseDown(const Point& pos, int key)
|
||||
bool Scrollbar::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
mouse_drag_start_pos = pos;
|
||||
|
||||
|
|
@ -253,11 +254,11 @@ bool Scrollbar::OnMouseDown(const Point& pos, int key)
|
|||
UpdatePartPositions();
|
||||
|
||||
Update();
|
||||
CaptureMouse();
|
||||
SetPointerCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scrollbar::OnMouseUp(const Point& pos, int key)
|
||||
bool Scrollbar::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
if (mouse_down_mode == mouse_down_thumb_drag)
|
||||
{
|
||||
|
|
@ -269,7 +270,7 @@ bool Scrollbar::OnMouseUp(const Point& pos, int key)
|
|||
mouse_down_timer->Stop();
|
||||
|
||||
Update();
|
||||
ReleaseMouseCapture();
|
||||
ReleasePointerCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -294,8 +295,8 @@ 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), Colorf::fromRgba8(33, 33, 33));
|
||||
canvas->fillRect(Rect::shrink(rect_thumb, 4.0, 0.0, 4.0, 0.0), Colorf::fromRgba8(58, 58, 58));
|
||||
canvas->fillRect(Rect::shrink(Rect::xywh(0.0, 0.0, GetWidth(), GetHeight()), 4.0, 0.0, 4.0, 0.0), GetStyleColor("track-color"));
|
||||
canvas->fillRect(Rect::shrink(rect_thumb, 4.0, 0.0, 4.0, 0.0), GetStyleColor("thumb-color"));
|
||||
}
|
||||
|
||||
// Calculates positions of all parts. Returns true if thumb position was changed compared to previously, false otherwise.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
Statusbar::Statusbar(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetStyleClass("statusbar");
|
||||
|
||||
CommandEdit = new LineEdit(this);
|
||||
CommandEdit->SetFrameGeometry(Rect::xywh(90.0, 4.0, 400.0, 23.0));
|
||||
}
|
||||
|
|
@ -15,5 +17,5 @@ Statusbar::~Statusbar()
|
|||
|
||||
void Statusbar::OnPaint(Canvas* canvas)
|
||||
{
|
||||
canvas->drawText(Point(16.0, 21.0), Colorf::fromRgba8(0, 0, 0), "Command:");
|
||||
canvas->drawText(Point(16.0, 21.0), Colorf::fromRgba8(226, 223, 219), "Command:");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ int TabWidget::GetPageIndex(Widget* pageWidget) const
|
|||
for (size_t i = 0; i < Pages.size(); i++)
|
||||
{
|
||||
if (Pages[i] == pageWidget)
|
||||
return i;
|
||||
return (int)i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -98,10 +98,6 @@ void TabWidget::OnBarCurrentChanged()
|
|||
OnCurrentChanged();
|
||||
}
|
||||
|
||||
void TabWidget::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
}
|
||||
|
||||
void TabWidget::OnGeometryChanged()
|
||||
{
|
||||
double w = GetWidth();
|
||||
|
|
@ -115,7 +111,7 @@ void TabWidget::OnGeometryChanged()
|
|||
|
||||
TabBar::TabBar(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetNoncontentSizes(20.0, 0.0, 20.0, 0.0);
|
||||
SetStyleClass("tabbar");
|
||||
}
|
||||
|
||||
int TabBar::AddTab(const std::string& label)
|
||||
|
|
@ -129,7 +125,7 @@ int TabBar::AddTab(const std::shared_ptr<Image>& icon, const std::string& label)
|
|||
tab->SetIcon(icon);
|
||||
tab->SetText(label);
|
||||
tab->OnClick = [=]() { OnTabClicked(tab); };
|
||||
int pageIndex = Tabs.size();
|
||||
int pageIndex = (int)Tabs.size();
|
||||
Tabs.push_back(tab);
|
||||
if (CurrentIndex == -1)
|
||||
SetCurrentIndex(pageIndex);
|
||||
|
|
@ -182,18 +178,11 @@ int TabBar::GetTabIndex(TabBarTab* tab)
|
|||
for (size_t i = 0; i < Tabs.size(); i++)
|
||||
{
|
||||
if (Tabs[i] == tab)
|
||||
return i;
|
||||
return (int)i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TabBar::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
double w = GetFrameGeometry().width;
|
||||
double h = GetFrameGeometry().height;
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(38, 38, 38));
|
||||
}
|
||||
|
||||
void TabBar::OnGeometryChanged()
|
||||
{
|
||||
double w = GetWidth();
|
||||
|
|
@ -211,7 +200,7 @@ void TabBar::OnGeometryChanged()
|
|||
|
||||
TabBarTab::TabBarTab(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetNoncontentSizes(15.0, 0.0, 15.0, 0.0);
|
||||
SetStyleClass("tabbar-tab");
|
||||
}
|
||||
|
||||
void TabBarTab::SetText(const std::string& text)
|
||||
|
|
@ -257,7 +246,7 @@ void TabBarTab::SetCurrent(bool value)
|
|||
if (IsCurrent != value)
|
||||
{
|
||||
IsCurrent = value;
|
||||
Update();
|
||||
SetStyleState(value ? "active" : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268,20 +257,6 @@ double TabBarTab::GetPreferredWidth() const
|
|||
return x;
|
||||
}
|
||||
|
||||
void TabBarTab::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
double w = GetFrameGeometry().width;
|
||||
double h = GetFrameGeometry().height;
|
||||
if (IsCurrent)
|
||||
{
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(51, 51, 51));
|
||||
}
|
||||
else if (hot)
|
||||
{
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(45, 45, 45));
|
||||
}
|
||||
}
|
||||
|
||||
void TabBarTab::OnGeometryChanged()
|
||||
{
|
||||
double x = 0.0;
|
||||
|
|
@ -300,28 +275,28 @@ void TabBarTab::OnGeometryChanged()
|
|||
|
||||
void TabBarTab::OnMouseMove(const Point& pos)
|
||||
{
|
||||
if (!hot)
|
||||
if (GetStyleState().empty())
|
||||
{
|
||||
hot = true;
|
||||
Update();
|
||||
SetStyleState("hover");
|
||||
}
|
||||
}
|
||||
|
||||
bool TabBarTab::OnMouseDown(const Point& pos, int key)
|
||||
bool TabBarTab::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
if (OnClick)
|
||||
OnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TabBarTab::OnMouseUp(const Point& pos, int key)
|
||||
bool TabBarTab::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void TabBarTab::OnMouseLeave()
|
||||
{
|
||||
hot = false;
|
||||
if (GetStyleState() == "hover")
|
||||
SetStyleState("");
|
||||
Update();
|
||||
}
|
||||
|
||||
|
|
@ -329,7 +304,7 @@ void TabBarTab::OnMouseLeave()
|
|||
|
||||
TabWidgetStack::TabWidgetStack(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetNoncontentSizes(20.0, 5.0, 20.0, 5.0);
|
||||
SetStyleClass("tabwidget-stack");
|
||||
}
|
||||
|
||||
void TabWidgetStack::SetCurrentWidget(Widget* widget)
|
||||
|
|
@ -347,10 +322,6 @@ void TabWidgetStack::SetCurrentWidget(Widget* widget)
|
|||
}
|
||||
}
|
||||
|
||||
void TabWidgetStack::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
}
|
||||
|
||||
void TabWidgetStack::OnGeometryChanged()
|
||||
{
|
||||
if (CurrentWidget)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "widgets/scrollbar/scrollbar.h"
|
||||
#include "core/utf8reader.h"
|
||||
#include "core/colorf.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4267) // warning C4267: 'initializing': conversion from 'size_t' to 'int', possible loss of data
|
||||
|
|
@ -10,7 +11,7 @@
|
|||
|
||||
TextEdit::TextEdit(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetNoncontentSizes(8.0, 8.0, 8.0, 8.0);
|
||||
SetStyleClass("textedit");
|
||||
|
||||
timer = new Timer(this);
|
||||
timer->FuncExpired = [=]() { OnTimerExpired(); };
|
||||
|
|
@ -286,11 +287,11 @@ void TextEdit::OnMouseMove(const Point& pos)
|
|||
}
|
||||
}
|
||||
|
||||
bool TextEdit::OnMouseDown(const Point& pos, int key)
|
||||
bool TextEdit::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
if (key == IK_LeftMouse)
|
||||
if (key == InputKey::LeftMouse)
|
||||
{
|
||||
CaptureMouse();
|
||||
SetPointerCapture();
|
||||
mouse_selecting = true;
|
||||
cursor_pos = GetCharacterIndex(pos);
|
||||
selection_start = cursor_pos;
|
||||
|
|
@ -301,25 +302,25 @@ bool TextEdit::OnMouseDown(const Point& pos, int key)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool TextEdit::OnMouseDoubleclick(const Point& pos, int key)
|
||||
bool TextEdit::OnMouseDoubleclick(const Point& pos, InputKey key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextEdit::OnMouseUp(const Point& pos, int key)
|
||||
bool TextEdit::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
if (mouse_selecting && key == IK_LeftMouse)
|
||||
if (mouse_selecting && key == InputKey::LeftMouse)
|
||||
{
|
||||
if (ignore_mouse_events) // This prevents text selection from changing from what was set when focus was gained.
|
||||
{
|
||||
ReleaseMouseCapture();
|
||||
ReleasePointerCapture();
|
||||
ignore_mouse_events = false;
|
||||
mouse_selecting = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
scroll_timer->Stop();
|
||||
ReleaseMouseCapture();
|
||||
ReleasePointerCapture();
|
||||
mouse_selecting = false;
|
||||
ivec2 sel_end = GetCharacterIndex(pos);
|
||||
selection_length = ToOffset(sel_end) - ToOffset(selection_start);
|
||||
|
|
@ -360,9 +361,9 @@ void TextEdit::OnKeyChar(std::string chars)
|
|||
}
|
||||
}
|
||||
|
||||
void TextEdit::OnKeyDown(EInputKey key)
|
||||
void TextEdit::OnKeyDown(InputKey key)
|
||||
{
|
||||
if (!readonly && key == IK_Enter)
|
||||
if (!readonly && key == InputKey::Enter)
|
||||
{
|
||||
if (FuncEnterPressed)
|
||||
{
|
||||
|
|
@ -383,17 +384,17 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
timer->Start(500); // don't blink cursor when moving or typing.
|
||||
}
|
||||
|
||||
if (key == IK_Enter || key == IK_Escape || key == IK_Tab)
|
||||
if (key == InputKey::Enter || key == InputKey::Escape || key == InputKey::Tab)
|
||||
{
|
||||
// Do not consume these.
|
||||
return;
|
||||
}
|
||||
else if (key == IK_A && GetKeyState(IK_Ctrl))
|
||||
else if (key == InputKey::A && GetKeyState(InputKey::Ctrl))
|
||||
{
|
||||
// select all
|
||||
SelectAll();
|
||||
}
|
||||
else if (key == IK_C && GetKeyState(IK_Ctrl))
|
||||
else if (key == InputKey::C && GetKeyState(InputKey::Ctrl))
|
||||
{
|
||||
std::string str = GetSelection();
|
||||
SetClipboardText(str);
|
||||
|
|
@ -403,9 +404,9 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
// Do not consume messages on read only component (only allow CTRL-A and CTRL-C)
|
||||
return;
|
||||
}
|
||||
else if (key == IK_Up)
|
||||
else if (key == InputKey::Up)
|
||||
{
|
||||
if (GetKeyState(IK_Shift) && selection_length == 0)
|
||||
if (GetKeyState(InputKey::Shift) && selection_length == 0)
|
||||
selection_start = cursor_pos;
|
||||
|
||||
if (cursor_pos.y > 0)
|
||||
|
|
@ -414,7 +415,7 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
cursor_pos.x = std::min(lines[cursor_pos.y].text.size(), (size_t)cursor_pos.x);
|
||||
}
|
||||
|
||||
if (GetKeyState(IK_Shift))
|
||||
if (GetKeyState(InputKey::Shift))
|
||||
{
|
||||
selection_length = ToOffset(cursor_pos) - ToOffset(selection_start);
|
||||
}
|
||||
|
|
@ -428,9 +429,9 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
Update();
|
||||
undo_info.first_text_insert = true;
|
||||
}
|
||||
else if (key == IK_Down)
|
||||
else if (key == InputKey::Down)
|
||||
{
|
||||
if (GetKeyState(IK_Shift) && selection_length == 0)
|
||||
if (GetKeyState(InputKey::Shift) && selection_length == 0)
|
||||
selection_start = cursor_pos;
|
||||
|
||||
if (cursor_pos.y < lines.size() - 1)
|
||||
|
|
@ -439,7 +440,7 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
cursor_pos.x = std::min(lines[cursor_pos.y].text.size(), (size_t)cursor_pos.x);
|
||||
}
|
||||
|
||||
if (GetKeyState(IK_Shift))
|
||||
if (GetKeyState(InputKey::Shift))
|
||||
{
|
||||
selection_length = ToOffset(cursor_pos) - ToOffset(selection_start);
|
||||
}
|
||||
|
|
@ -454,55 +455,55 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
Update();
|
||||
undo_info.first_text_insert = true;
|
||||
}
|
||||
else if (key == IK_Left)
|
||||
else if (key == InputKey::Left)
|
||||
{
|
||||
Move(-1, GetKeyState(IK_Shift), GetKeyState(IK_Ctrl));
|
||||
Move(-1, GetKeyState(InputKey::Shift), GetKeyState(InputKey::Ctrl));
|
||||
}
|
||||
else if (key == IK_Right)
|
||||
else if (key == InputKey::Right)
|
||||
{
|
||||
Move(1, GetKeyState(IK_Shift), GetKeyState(IK_Ctrl));
|
||||
Move(1, GetKeyState(InputKey::Shift), GetKeyState(InputKey::Ctrl));
|
||||
}
|
||||
else if (key == IK_Backspace)
|
||||
else if (key == InputKey::Backspace)
|
||||
{
|
||||
Backspace();
|
||||
}
|
||||
else if (key == IK_Delete)
|
||||
else if (key == InputKey::Delete)
|
||||
{
|
||||
Del();
|
||||
}
|
||||
else if (key == IK_Home)
|
||||
else if (key == InputKey::Home)
|
||||
{
|
||||
if (GetKeyState(IK_Ctrl))
|
||||
if (GetKeyState(InputKey::Ctrl))
|
||||
cursor_pos = ivec2(0, 0);
|
||||
else
|
||||
cursor_pos.x = 0;
|
||||
if (GetKeyState(IK_Shift))
|
||||
if (GetKeyState(InputKey::Shift))
|
||||
selection_length = ToOffset(cursor_pos) - ToOffset(selection_start);
|
||||
else
|
||||
ClearSelection();
|
||||
Update();
|
||||
MoveVerticalScroll();
|
||||
}
|
||||
else if (key == IK_End)
|
||||
else if (key == InputKey::End)
|
||||
{
|
||||
if (GetKeyState(IK_Ctrl))
|
||||
if (GetKeyState(InputKey::Ctrl))
|
||||
cursor_pos = ivec2(lines.back().text.length(), lines.size() - 1);
|
||||
else
|
||||
cursor_pos.x = lines[cursor_pos.y].text.size();
|
||||
|
||||
if (GetKeyState(IK_Shift))
|
||||
if (GetKeyState(InputKey::Shift))
|
||||
selection_length = ToOffset(cursor_pos) - ToOffset(selection_start);
|
||||
else
|
||||
ClearSelection();
|
||||
Update();
|
||||
}
|
||||
else if (key == IK_X && GetKeyState(IK_Ctrl))
|
||||
else if (key == InputKey::X && GetKeyState(InputKey::Ctrl))
|
||||
{
|
||||
std::string str = GetSelection();
|
||||
DeleteSelectedText();
|
||||
SetClipboardText(str);
|
||||
}
|
||||
else if (key == IK_V && GetKeyState(IK_Ctrl))
|
||||
else if (key == InputKey::V && GetKeyState(InputKey::Ctrl))
|
||||
{
|
||||
std::string str = GetClipboardText();
|
||||
std::string::const_iterator end_str = std::remove(str.begin(), str.end(), '\r');
|
||||
|
|
@ -524,7 +525,7 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
}
|
||||
MoveVerticalScroll();
|
||||
}
|
||||
else if (GetKeyState(IK_Ctrl) && key == IK_Z)
|
||||
else if (GetKeyState(InputKey::Ctrl) && key == InputKey::Z)
|
||||
{
|
||||
if (!readonly)
|
||||
{
|
||||
|
|
@ -533,7 +534,7 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
SetText(tmp);
|
||||
}
|
||||
}
|
||||
else if (key == IK_Shift)
|
||||
else if (key == InputKey::Shift)
|
||||
{
|
||||
if (selection_length == 0)
|
||||
selection_start = cursor_pos;
|
||||
|
|
@ -543,7 +544,7 @@ void TextEdit::OnKeyDown(EInputKey key)
|
|||
FuncAfterEditChanged();
|
||||
}
|
||||
|
||||
void TextEdit::OnKeyUp(EInputKey key)
|
||||
void TextEdit::OnKeyUp(InputKey key)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -961,17 +962,18 @@ void TextEdit::LayoutLines(Canvas* canvas)
|
|||
sel_end = selection_start;
|
||||
}
|
||||
|
||||
Colorf textColor = GetStyleColor("color");
|
||||
Point draw_pos;
|
||||
for (size_t i = vert_scrollbar->GetPosition(); i < lines.size(); i++)
|
||||
for (size_t i = (size_t)vert_scrollbar->GetPosition(); i < lines.size(); i++)
|
||||
{
|
||||
Line& line = lines[i];
|
||||
if (line.invalidated)
|
||||
{
|
||||
line.layout.Clear();
|
||||
if (!line.text.empty())
|
||||
line.layout.AddText(line.text, font, Colorf::fromRgba8(255, 255, 255));
|
||||
line.layout.AddText(line.text, font, textColor);
|
||||
else
|
||||
line.layout.AddText(" ", font, Colorf::fromRgba8(255, 255, 255)); // Draw one space character to get the correct height
|
||||
line.layout.AddText(" ", font, textColor); // Draw one space character to get the correct height
|
||||
line.layout.Layout(canvas, GetWidth());
|
||||
line.box = Rect(draw_pos, line.layout.GetSize());
|
||||
line.invalidated = false;
|
||||
|
|
@ -992,7 +994,7 @@ void TextEdit::LayoutLines(Canvas* canvas)
|
|||
if (cursor_blink_visible && cursor_pos.y == i)
|
||||
{
|
||||
line.layout.SetCursorPos(cursor_pos.x);
|
||||
line.layout.SetCursorColor(Colorf::fromRgba8(255, 255, 255));
|
||||
line.layout.SetCursorColor(textColor);
|
||||
line.layout.ShowCursor();
|
||||
}
|
||||
}
|
||||
|
|
@ -1006,22 +1008,10 @@ void TextEdit::LayoutLines(Canvas* canvas)
|
|||
UpdateVerticalScroll();
|
||||
}
|
||||
|
||||
void TextEdit::OnPaintFrame(Canvas* canvas)
|
||||
{
|
||||
double w = GetFrameGeometry().width;
|
||||
double h = GetFrameGeometry().height;
|
||||
Colorf bordercolor = Colorf::fromRgba8(100, 100, 100);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, h), Colorf::fromRgba8(38, 38, 38));
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, h - 1.0, w, 1.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(0.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
canvas->fillRect(Rect::xywh(w - 1.0, 0.0, 1.0, h - 0.0), bordercolor);
|
||||
}
|
||||
|
||||
void TextEdit::OnPaint(Canvas* canvas)
|
||||
{
|
||||
LayoutLines(canvas);
|
||||
for (size_t i = vert_scrollbar->GetPosition(); i < lines.size(); i++)
|
||||
for (size_t i = (size_t)vert_scrollbar->GetPosition(); i < lines.size(); i++)
|
||||
lines[i].layout.DrawLayout(canvas);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,5 +56,5 @@ void TextLabel::OnPaint(Canvas* canvas)
|
|||
x = GetWidth() - canvas->measureText(text).width;
|
||||
}
|
||||
|
||||
canvas->drawText(Point(x, GetHeight() - 5.0), Colorf::fromRgba8(255, 255, 255), text);
|
||||
canvas->drawText(Point(x, GetHeight() - 5.0), GetStyleColor("color"), text);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,63 @@
|
|||
|
||||
#include "widgets/toolbar/toolbar.h"
|
||||
#include "widgets/toolbar/toolbarbutton.h"
|
||||
|
||||
Toolbar::Toolbar(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetStyleClass("toolbar");
|
||||
}
|
||||
|
||||
Toolbar::~Toolbar()
|
||||
{
|
||||
}
|
||||
|
||||
void Toolbar::SetDirection(ToolbarDirection newDirection)
|
||||
{
|
||||
if (direction != newDirection)
|
||||
{
|
||||
direction = newDirection;
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarButton* Toolbar::AddButton(std::string icon, std::string text, std::function<void()> onClicked)
|
||||
{
|
||||
ToolbarButton* button = new ToolbarButton(this);
|
||||
if (!icon.empty())
|
||||
button->SetIcon(icon);
|
||||
if (!text.empty())
|
||||
button->SetText(text);
|
||||
button->OnClick = std::move(onClicked);
|
||||
buttons.push_back(button);
|
||||
return button;
|
||||
}
|
||||
|
||||
void Toolbar::OnGeometryChanged()
|
||||
{
|
||||
if (direction == ToolbarDirection::Horizontal)
|
||||
{
|
||||
double x = 7.0;
|
||||
double barHeight = GetHeight();
|
||||
double gap = 7.0;
|
||||
for (ToolbarButton* button : buttons)
|
||||
{
|
||||
double width = button->GetPreferredWidth();
|
||||
double height = button->GetPreferredHeight();
|
||||
button->SetFrameGeometry(Rect::xywh(x, (barHeight - height) * 0.5, width, height));
|
||||
x += width + gap;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double y = 7.0;
|
||||
double barWidth = GetWidth();
|
||||
double gap = 7.0;
|
||||
for (ToolbarButton* button : buttons)
|
||||
{
|
||||
double width = button->GetPreferredWidth();
|
||||
double height = button->GetPreferredHeight();
|
||||
button->SetFrameGeometry(Rect::xywh((barWidth - width) * 0.5, y, width, height));
|
||||
y += height + gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,112 @@
|
|||
|
||||
ToolbarButton::ToolbarButton(Widget* parent) : Widget(parent)
|
||||
{
|
||||
SetStyleClass("toolbarbutton");
|
||||
}
|
||||
|
||||
ToolbarButton::~ToolbarButton()
|
||||
{
|
||||
}
|
||||
|
||||
void ToolbarButton::OnPaint(Canvas* canvas)
|
||||
void ToolbarButton::SetIcon(std::string icon)
|
||||
{
|
||||
if (!icon.empty())
|
||||
{
|
||||
if (!image)
|
||||
image = new ImageBox(this);
|
||||
image->SetImage(Image::LoadResource(icon, GetDpiScale()));
|
||||
image->SetImageMode(ImageBoxMode::Contain);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete image;
|
||||
image = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ToolbarButton::SetText(std::string text)
|
||||
{
|
||||
if (!text.empty())
|
||||
{
|
||||
if (!label)
|
||||
label = new TextLabel(this);
|
||||
label->SetText(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete label;
|
||||
label = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ToolbarButton::Click()
|
||||
{
|
||||
if (OnClick)
|
||||
OnClick();
|
||||
}
|
||||
|
||||
double ToolbarButton::GetPreferredWidth()
|
||||
{
|
||||
double w = 0.0;
|
||||
if (image)
|
||||
w = 26.0;
|
||||
if (label)
|
||||
w += label->GetPreferredWidth();
|
||||
return w;
|
||||
}
|
||||
|
||||
double ToolbarButton::GetPreferredHeight()
|
||||
{
|
||||
double h = 0.0;
|
||||
if (image)
|
||||
h = 24.0;
|
||||
if (label)
|
||||
h = std::max(h, label->GetPreferredHeight());
|
||||
return h;
|
||||
}
|
||||
|
||||
void ToolbarButton::OnMouseMove(const Point& pos)
|
||||
{
|
||||
if (GetStyleState().empty())
|
||||
{
|
||||
SetStyleState("hover");
|
||||
}
|
||||
}
|
||||
|
||||
void ToolbarButton::OnMouseLeave()
|
||||
{
|
||||
SetStyleState("");
|
||||
}
|
||||
|
||||
bool ToolbarButton::OnMouseDown(const Point& pos, InputKey key)
|
||||
{
|
||||
SetStyleState("down");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ToolbarButton::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
if (GetStyleState() == "down")
|
||||
{
|
||||
SetStyleState("");
|
||||
Repaint();
|
||||
Click();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ToolbarButton::OnGeometryChanged()
|
||||
{
|
||||
double totalHeight = GetPreferredHeight();
|
||||
double x = 0.0;
|
||||
if (image)
|
||||
{
|
||||
image->SetFrameGeometry(Rect::xywh(0.0, (totalHeight - 24.0) * 0.5, 24.0, 24.0));
|
||||
x += 26.0;
|
||||
}
|
||||
if (label)
|
||||
{
|
||||
double labelHeight = label->GetPreferredHeight();
|
||||
label->SetFrameGeometry(Rect::xywh(x, (totalHeight - labelHeight) * 0.5, GetWidth() - x, labelHeight));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
288
libraries/ZWidget/src/window/dbus/dbus_open_file_dialog.cpp
Normal file
288
libraries/ZWidget/src/window/dbus/dbus_open_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
|
||||
#include "dbus_open_file_dialog.h"
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
DBusOpenFileDialog::DBusOpenFileDialog(std::string ownerHandle) : ownerHandle(ownerHandle)
|
||||
{
|
||||
}
|
||||
|
||||
std::string DBusOpenFileDialog::Filename() const
|
||||
{
|
||||
return outputFilenames.empty() ? std::string() : outputFilenames.front();
|
||||
}
|
||||
|
||||
std::vector<std::string> DBusOpenFileDialog::Filenames() const
|
||||
{
|
||||
return outputFilenames;
|
||||
}
|
||||
|
||||
void DBusOpenFileDialog::SetMultiSelect(bool multiselect)
|
||||
{
|
||||
this->multiSelect = multiSelect;
|
||||
}
|
||||
|
||||
void DBusOpenFileDialog::SetFilename(const std::string &filename)
|
||||
{
|
||||
inputFilename = filename;
|
||||
}
|
||||
|
||||
void DBusOpenFileDialog::SetDefaultExtension(const std::string& extension)
|
||||
{
|
||||
defaultExt = extension;
|
||||
}
|
||||
|
||||
void DBusOpenFileDialog::AddFilter(const std::string &filter_description, const std::string &filter_extension)
|
||||
{
|
||||
filters.push_back({ filter_description, filter_extension });
|
||||
}
|
||||
|
||||
void DBusOpenFileDialog::ClearFilters()
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void DBusOpenFileDialog::SetFilterIndex(int filter_index)
|
||||
{
|
||||
this->filter_index = filter_index;
|
||||
}
|
||||
|
||||
void DBusOpenFileDialog::SetInitialDirectory(const std::string &path)
|
||||
{
|
||||
initialDirectory = path;
|
||||
}
|
||||
|
||||
void DBusOpenFileDialog::SetTitle(const std::string &title)
|
||||
{
|
||||
this->title = title;
|
||||
}
|
||||
|
||||
bool DBusOpenFileDialog::Show()
|
||||
{
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileChooser.html
|
||||
|
||||
dbus_bool_t bresult = {};
|
||||
DBusError error = {};
|
||||
dbus_error_init(&error);
|
||||
|
||||
DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
|
||||
if (!connection)
|
||||
{
|
||||
dbus_error_free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string busname = dbus_bus_get_unique_name(connection);
|
||||
|
||||
DBusMessage* request = dbus_message_new_method_call("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.FileChooser", "OpenFile");
|
||||
if (!request)
|
||||
{
|
||||
dbus_connection_unref(connection);
|
||||
dbus_error_free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* parentWindow = ownerHandle.c_str();
|
||||
const char* title = this->title.c_str();
|
||||
|
||||
DBusMessageIter requestArgs = {};
|
||||
dbus_message_iter_init_append(request, &requestArgs);
|
||||
|
||||
bresult = dbus_message_iter_append_basic(&requestArgs, DBUS_TYPE_STRING, &parentWindow);
|
||||
bresult = dbus_message_iter_append_basic(&requestArgs, DBUS_TYPE_STRING, &title);
|
||||
|
||||
DBusMessageIter requestOptions = {};
|
||||
bresult = dbus_message_iter_open_container(&requestArgs, DBUS_TYPE_ARRAY, "{sv}", &requestOptions);
|
||||
|
||||
// handle_token - s race condition prevention for signal (/org/freedesktop/portal/desktop/request/SENDER/TOKEN)
|
||||
// accept_label - s text label for the OK button
|
||||
// modal - b makes dialog modal. Defaults to true. Not really sure what it means since nothing stayed modal on my computer
|
||||
// directory - b open folder mode, added in version 3
|
||||
// filters - a(sa(us)) [('Images', [(0, '*.ico'), (1, 'image/png')]), ('Text', [(0, '*.txt')])]
|
||||
// current_filter - sa(us) filter from filters that should be current filter
|
||||
// choices - a(ssa(ss)s) list of serialized combo boxes to add to the file chooser
|
||||
// current_name - s suggested name
|
||||
|
||||
// current_folder - ay
|
||||
if (!initialDirectory.empty())
|
||||
{
|
||||
// Note: the docs unfortunately says "The portal implementation is free to ignore this option"
|
||||
|
||||
const char* key = "current_folder";
|
||||
const char* value = initialDirectory.c_str();
|
||||
int valueCount = (int)initialDirectory.size() + 1;
|
||||
|
||||
DBusMessageIter entry = {};
|
||||
bresult = dbus_message_iter_open_container(&requestOptions, DBUS_TYPE_DICT_ENTRY, nullptr, &entry);
|
||||
bresult = dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
|
||||
|
||||
DBusMessageIter variant = {};
|
||||
DBusMessageIter array = {};
|
||||
bresult = dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "ay", &variant);
|
||||
bresult = dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array);
|
||||
bresult = dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &value, valueCount);
|
||||
bresult = dbus_message_iter_close_container(&variant, &array);
|
||||
bresult = dbus_message_iter_close_container(&entry, &variant);
|
||||
|
||||
bresult = dbus_message_iter_close_container(&requestOptions, &entry);
|
||||
}
|
||||
|
||||
// multiple - b
|
||||
{
|
||||
const char* key = "multiple";
|
||||
dbus_bool_t value = multiSelect;
|
||||
|
||||
DBusMessageIter entry = {};
|
||||
bresult = dbus_message_iter_open_container(&requestOptions, DBUS_TYPE_DICT_ENTRY, nullptr, &entry);
|
||||
bresult = dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
|
||||
|
||||
DBusMessageIter variant = {};
|
||||
bresult = dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, DBUS_TYPE_BOOLEAN_AS_STRING, &variant);
|
||||
bresult = dbus_message_iter_append_basic(&variant, DBUS_TYPE_BOOLEAN, &value);
|
||||
bresult = dbus_message_iter_close_container(&entry, &variant);
|
||||
|
||||
bresult = dbus_message_iter_close_container(&requestOptions, &entry);
|
||||
}
|
||||
|
||||
bresult = dbus_message_iter_close_container(&requestArgs, &requestOptions);
|
||||
|
||||
DBusMessage* response = dbus_connection_send_with_reply_and_block(connection, request, DBUS_TIMEOUT_USE_DEFAULT, &error);
|
||||
if (!response)
|
||||
{
|
||||
dbus_message_unref(request);
|
||||
dbus_connection_unref(connection);
|
||||
dbus_error_free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* handle = nullptr;
|
||||
bresult = dbus_message_get_args(response, &error, DBUS_TYPE_OBJECT_PATH, &handle, DBUS_TYPE_INVALID);
|
||||
if (!bresult)
|
||||
{
|
||||
dbus_message_unref(response);
|
||||
dbus_message_unref(request);
|
||||
dbus_connection_unref(connection);
|
||||
dbus_error_free(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string signalObjectPath = handle;
|
||||
|
||||
dbus_message_unref(response);
|
||||
dbus_message_unref(request);
|
||||
|
||||
std::string rule = "type='signal',interface='org.freedesktop.portal.Request',member='Response',path='" + signalObjectPath + "'";
|
||||
dbus_bus_add_match(connection, rule.c_str(), &error);
|
||||
|
||||
// Wait for the response signal
|
||||
//
|
||||
// To do: process the run loop while we wait
|
||||
//
|
||||
DBusMessage* signalmsg = nullptr;
|
||||
while (!signalmsg && dbus_connection_read_write(connection, -1))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
DBusMessage* message = dbus_connection_pop_message(connection);
|
||||
if (!message)
|
||||
break;
|
||||
|
||||
if (dbus_message_is_signal(message, "org.freedesktop.portal.Request", "Response") && dbus_message_get_path(message) == signalObjectPath)
|
||||
{
|
||||
signalmsg = message;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbus_message_unref(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
dbus_bus_remove_match(connection, rule.c_str(), &error);
|
||||
|
||||
// Read the response
|
||||
|
||||
dbus_uint32_t responseCode = 0;
|
||||
std::vector<std::string> uris;
|
||||
|
||||
DBusMessageIter signalArgs;
|
||||
bresult = dbus_message_iter_init(signalmsg, &signalArgs);
|
||||
|
||||
// response code - u
|
||||
if (dbus_message_iter_get_arg_type(&signalArgs) == DBUS_TYPE_UINT32)
|
||||
{
|
||||
dbus_message_iter_get_basic(&signalArgs, &responseCode);
|
||||
}
|
||||
dbus_message_iter_next(&signalArgs);
|
||||
|
||||
// results - a{sv}
|
||||
if (dbus_message_iter_get_arg_type(&signalArgs) == DBUS_TYPE_ARRAY)
|
||||
{
|
||||
DBusMessageIter resultsArray = {};
|
||||
dbus_message_iter_recurse(&signalArgs, &resultsArray);
|
||||
while (true)
|
||||
{
|
||||
int type = dbus_message_iter_get_arg_type(&resultsArray);
|
||||
if (type != DBUS_TYPE_DICT_ENTRY)
|
||||
break;
|
||||
|
||||
DBusMessageIter entry = {};
|
||||
dbus_message_iter_recurse(&resultsArray, &entry);
|
||||
|
||||
const char* key = nullptr;
|
||||
dbus_message_iter_get_basic(&entry, &key);
|
||||
dbus_message_iter_next(&entry);
|
||||
|
||||
DBusMessageIter value = {};
|
||||
dbus_message_iter_recurse(&entry, &value);
|
||||
|
||||
std::string k = key;
|
||||
if (k == "uris" && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_ARRAY) // as
|
||||
{
|
||||
DBusMessageIter uriArray = {};
|
||||
dbus_message_iter_recurse(&value, &uriArray);
|
||||
while (true)
|
||||
{
|
||||
int type = dbus_message_iter_get_arg_type(&uriArray);
|
||||
if (type != DBUS_TYPE_STRING)
|
||||
break;
|
||||
|
||||
const char* uri = nullptr;
|
||||
dbus_message_iter_get_basic(&uriArray, &uri);
|
||||
uris.push_back(uri);
|
||||
|
||||
dbus_message_iter_next(&uriArray);
|
||||
}
|
||||
}
|
||||
else if (k == "choices" && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_ARRAY) // a(ss)
|
||||
{
|
||||
// An array of pairs of strings,
|
||||
// the first string being the ID of a combobox that was passed into this call,
|
||||
// the second string being the selected option.
|
||||
}
|
||||
else if (k == "current_filter" && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRUCT) // sa(us)
|
||||
{
|
||||
// The filter that was selected.
|
||||
// This may match a filter in the filter list or another filter that was applied unconditionally.
|
||||
}
|
||||
|
||||
dbus_message_iter_next(&entry);
|
||||
dbus_message_iter_next(&resultsArray);
|
||||
}
|
||||
}
|
||||
dbus_message_iter_next(&signalArgs);
|
||||
|
||||
dbus_message_unref(signalmsg);
|
||||
dbus_connection_unref(connection);
|
||||
dbus_error_free(&error);
|
||||
|
||||
if (responseCode != 0) // User cancelled
|
||||
return false;
|
||||
|
||||
for (const std::string& uri : uris)
|
||||
{
|
||||
if (uri.size() > 7 && uri.substr(0, 7) == "file://")
|
||||
outputFilenames.push_back(uri.substr(7));
|
||||
}
|
||||
|
||||
return !uris.empty();
|
||||
}
|
||||
38
libraries/ZWidget/src/window/dbus/dbus_open_file_dialog.h
Normal file
38
libraries/ZWidget/src/window/dbus/dbus_open_file_dialog.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/open_file_dialog.h"
|
||||
|
||||
class DBusOpenFileDialog : public OpenFileDialog
|
||||
{
|
||||
public:
|
||||
DBusOpenFileDialog(std::string ownerHandle);
|
||||
|
||||
std::string Filename() const override;
|
||||
std::vector<std::string> Filenames() const override;
|
||||
void SetMultiSelect(bool multiselect) override;
|
||||
void SetFilename(const std::string &filename) override;
|
||||
void SetDefaultExtension(const std::string& extension) override;
|
||||
void AddFilter(const std::string &filter_description, const std::string &filter_extension) override;
|
||||
void ClearFilters() override;
|
||||
void SetFilterIndex(int filter_index) override;
|
||||
void SetInitialDirectory(const std::string &path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
bool Show() override;
|
||||
|
||||
private:
|
||||
std::string ownerHandle;
|
||||
std::string title;
|
||||
std::string initialDirectory;
|
||||
std::string inputFilename;
|
||||
std::string defaultExt;
|
||||
std::vector<std::string> outputFilenames;
|
||||
bool multiSelect = false;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string description;
|
||||
std::string extension;
|
||||
};
|
||||
std::vector<Filter> filters;
|
||||
int filter_index = 0;
|
||||
};
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
#include "dbus_open_folder_dialog.h"
|
||||
|
||||
DBusOpenFolderDialog::DBusOpenFolderDialog(std::string ownerHandle) : ownerHandle(ownerHandle)
|
||||
{
|
||||
}
|
||||
|
||||
bool DBusOpenFolderDialog::Show()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string DBusOpenFolderDialog::SelectedPath() const
|
||||
{
|
||||
return selected_path;
|
||||
}
|
||||
|
||||
void DBusOpenFolderDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void DBusOpenFolderDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
21
libraries/ZWidget/src/window/dbus/dbus_open_folder_dialog.h
Normal file
21
libraries/ZWidget/src/window/dbus/dbus_open_folder_dialog.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/open_folder_dialog.h"
|
||||
|
||||
class DBusOpenFolderDialog : public OpenFolderDialog
|
||||
{
|
||||
public:
|
||||
DBusOpenFolderDialog(std::string ownerHandle);
|
||||
|
||||
bool Show() override;
|
||||
std::string SelectedPath() const override;
|
||||
void SetInitialDirectory(const std::string& path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
|
||||
private:
|
||||
std::string ownerHandle;
|
||||
|
||||
std::string selected_path;
|
||||
std::string initial_directory;
|
||||
std::string title;
|
||||
};
|
||||
54
libraries/ZWidget/src/window/dbus/dbus_save_file_dialog.cpp
Normal file
54
libraries/ZWidget/src/window/dbus/dbus_save_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
#include "dbus_save_file_dialog.h"
|
||||
|
||||
DBusSaveFileDialog::DBusSaveFileDialog(std::string ownerHandle) : ownerHandle(ownerHandle)
|
||||
{
|
||||
}
|
||||
|
||||
bool DBusSaveFileDialog::Show()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string DBusSaveFileDialog::Filename() const
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
void DBusSaveFileDialog::SetFilename(const std::string& filename)
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void DBusSaveFileDialog::AddFilter(const std::string& filter_description, const std::string& filter_extension)
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void DBusSaveFileDialog::ClearFilters()
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void DBusSaveFileDialog::SetFilterIndex(int filter_index)
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void DBusSaveFileDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void DBusSaveFileDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void DBusSaveFileDialog::SetDefaultExtension(const std::string& extension)
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
37
libraries/ZWidget/src/window/dbus/dbus_save_file_dialog.h
Normal file
37
libraries/ZWidget/src/window/dbus/dbus_save_file_dialog.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/save_file_dialog.h"
|
||||
|
||||
class DBusSaveFileDialog : public SaveFileDialog
|
||||
{
|
||||
public:
|
||||
DBusSaveFileDialog(std::string ownerHandle);
|
||||
|
||||
bool Show() override;
|
||||
std::string Filename() const override;
|
||||
void SetFilename(const std::string& filename) override;
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override;
|
||||
void ClearFilters() override;
|
||||
void SetFilterIndex(int filter_index) override;
|
||||
void SetInitialDirectory(const std::string& path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
void SetDefaultExtension(const std::string& extension) override;
|
||||
|
||||
private:
|
||||
std::string ownerHandle;
|
||||
|
||||
std::string filename;
|
||||
std::string initial_directory;
|
||||
std::string initial_filename;
|
||||
std::string title;
|
||||
std::vector<std::string> filenames;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string description;
|
||||
std::string extension;
|
||||
};
|
||||
std::vector<Filter> filters;
|
||||
int filterindex = 0;
|
||||
std::string defaultext;
|
||||
};
|
||||
38
libraries/ZWidget/src/window/sdl2/sdl2_display_backend.cpp
Normal file
38
libraries/ZWidget/src/window/sdl2/sdl2_display_backend.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
#include "sdl2_display_backend.h"
|
||||
#include "sdl2_display_window.h"
|
||||
|
||||
std::unique_ptr<DisplayWindow> SDL2DisplayBackend::Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI)
|
||||
{
|
||||
return std::make_unique<SDL2DisplayWindow>(windowHost, popupWindow, static_cast<SDL2DisplayWindow*>(owner), renderAPI);
|
||||
}
|
||||
|
||||
void SDL2DisplayBackend::ProcessEvents()
|
||||
{
|
||||
SDL2DisplayWindow::ProcessEvents();
|
||||
}
|
||||
|
||||
void SDL2DisplayBackend::RunLoop()
|
||||
{
|
||||
SDL2DisplayWindow::RunLoop();
|
||||
}
|
||||
|
||||
void SDL2DisplayBackend::ExitLoop()
|
||||
{
|
||||
SDL2DisplayWindow::ExitLoop();
|
||||
}
|
||||
|
||||
Size SDL2DisplayBackend::GetScreenSize()
|
||||
{
|
||||
return SDL2DisplayWindow::GetScreenSize();
|
||||
}
|
||||
|
||||
void* SDL2DisplayBackend::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
return SDL2DisplayWindow::StartTimer(timeoutMilliseconds, std::move(onTimer));
|
||||
}
|
||||
|
||||
void SDL2DisplayBackend::StopTimer(void* timerID)
|
||||
{
|
||||
SDL2DisplayWindow::StopTimer(timerID);
|
||||
}
|
||||
19
libraries/ZWidget/src/window/sdl2/sdl2_display_backend.h
Normal file
19
libraries/ZWidget/src/window/sdl2/sdl2_display_backend.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "window/window.h"
|
||||
|
||||
class SDL2DisplayBackend : public DisplayBackend
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI) override;
|
||||
void ProcessEvents() override;
|
||||
void RunLoop() override;
|
||||
void ExitLoop() override;
|
||||
|
||||
void* StartTimer(int timeoutMilliseconds, std::function<void()> onTimer) override;
|
||||
void StopTimer(void* timerID) override;
|
||||
|
||||
Size GetScreenSize() override;
|
||||
|
||||
bool IsSDL2() override { return true; }
|
||||
};
|
||||
772
libraries/ZWidget/src/window/sdl2/sdl2_display_window.cpp
Normal file
772
libraries/ZWidget/src/window/sdl2/sdl2_display_window.cpp
Normal file
|
|
@ -0,0 +1,772 @@
|
|||
|
||||
#include "sdl2_display_window.h"
|
||||
#include <stdexcept>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
Uint32 SDL2DisplayWindow::PaintEventNumber = 0xffffffff;
|
||||
bool SDL2DisplayWindow::ExitRunLoop;
|
||||
std::unordered_map<int, SDL2DisplayWindow*> SDL2DisplayWindow::WindowList;
|
||||
|
||||
class InitSDL
|
||||
{
|
||||
public:
|
||||
InitSDL()
|
||||
{
|
||||
int result = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
|
||||
if (result != 0)
|
||||
throw std::runtime_error(std::string("Unable to initialize SDL:") + SDL_GetError());
|
||||
|
||||
SDL2DisplayWindow::PaintEventNumber = SDL_RegisterEvents(1);
|
||||
}
|
||||
};
|
||||
|
||||
static void CheckInitSDL()
|
||||
{
|
||||
static InitSDL initsdl;
|
||||
}
|
||||
|
||||
SDL2DisplayWindow::SDL2DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, SDL2DisplayWindow* owner, RenderAPI renderAPI) : WindowHost(windowHost)
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
unsigned int flags = SDL_WINDOW_HIDDEN /*| SDL_WINDOW_ALLOW_HIGHDPI*/;
|
||||
if (renderAPI == RenderAPI::Vulkan)
|
||||
flags |= SDL_WINDOW_VULKAN;
|
||||
else if (renderAPI == RenderAPI::OpenGL)
|
||||
flags |= SDL_WINDOW_OPENGL;
|
||||
#if defined(__APPLE__)
|
||||
else if (renderAPI == RenderAPI::Metal)
|
||||
flags |= SDL_WINDOW_METAL;
|
||||
#endif
|
||||
if (popupWindow)
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
|
||||
if (renderAPI == RenderAPI::Vulkan || renderAPI == RenderAPI::OpenGL || renderAPI == RenderAPI::Metal)
|
||||
{
|
||||
Handle.window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 320, 200, flags);
|
||||
if (!Handle.window)
|
||||
throw std::runtime_error(std::string("Unable to create SDL window:") + SDL_GetError());
|
||||
}
|
||||
else
|
||||
{
|
||||
int result = SDL_CreateWindowAndRenderer(320, 200, flags, &Handle.window, &RendererHandle);
|
||||
if (result != 0)
|
||||
throw std::runtime_error(std::string("Unable to create SDL window:") + SDL_GetError());
|
||||
}
|
||||
|
||||
WindowList[SDL_GetWindowID(Handle.window)] = this;
|
||||
}
|
||||
|
||||
SDL2DisplayWindow::~SDL2DisplayWindow()
|
||||
{
|
||||
UnlockCursor();
|
||||
|
||||
WindowList.erase(WindowList.find(SDL_GetWindowID(Handle.window)));
|
||||
|
||||
if (BackBufferTexture)
|
||||
{
|
||||
SDL_DestroyTexture(BackBufferTexture);
|
||||
BackBufferTexture = nullptr;
|
||||
}
|
||||
|
||||
if (RendererHandle)
|
||||
SDL_DestroyRenderer(RendererHandle);
|
||||
SDL_DestroyWindow(Handle.window);
|
||||
RendererHandle = nullptr;
|
||||
Handle.window = nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> SDL2DisplayWindow::GetVulkanInstanceExtensions()
|
||||
{
|
||||
unsigned int extCount = 0;
|
||||
SDL_Vulkan_GetInstanceExtensions(Handle.window, &extCount, nullptr);
|
||||
std::vector<const char*> extNames(extCount);
|
||||
SDL_Vulkan_GetInstanceExtensions(Handle.window, &extCount, extNames.data());
|
||||
|
||||
std::vector<std::string> result;
|
||||
result.reserve(extNames.size());
|
||||
for (const char* ext : extNames)
|
||||
result.emplace_back(ext);
|
||||
return result;
|
||||
}
|
||||
|
||||
VkSurfaceKHR SDL2DisplayWindow::CreateVulkanSurface(VkInstance instance)
|
||||
{
|
||||
VkSurfaceKHR surfaceHandle = {};
|
||||
SDL_Vulkan_CreateSurface(Handle.window, instance, &surfaceHandle);
|
||||
if (surfaceHandle)
|
||||
throw std::runtime_error("Could not create vulkan surface");
|
||||
return surfaceHandle;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetWindowTitle(const std::string& text)
|
||||
{
|
||||
SDL_SetWindowTitle(Handle.window, text.c_str());
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetWindowFrame(const Rect& box)
|
||||
{
|
||||
// SDL2 doesn't really seem to have an API for this.
|
||||
// The docs aren't clear what you're setting when calling SDL_SetWindowSize.
|
||||
SetClientFrame(box);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetClientFrame(const Rect& box)
|
||||
{
|
||||
// Is there a way to set both in one call?
|
||||
|
||||
double uiscale = GetDpiScale();
|
||||
int x = (int)std::round(box.x * uiscale);
|
||||
int y = (int)std::round(box.y * uiscale);
|
||||
int w = (int)std::round(box.width * uiscale);
|
||||
int h = (int)std::round(box.height * uiscale);
|
||||
|
||||
SDL_SetWindowPosition(Handle.window, x, y);
|
||||
SDL_SetWindowSize(Handle.window, w, h);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Show()
|
||||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowFullscreen()
|
||||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_SetWindowFullscreen(Handle.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
isFullscreen = true;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowMaximized()
|
||||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_MaximizeWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowMinimized()
|
||||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_MinimizeWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowNormal()
|
||||
{
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_SetWindowFullscreen(Handle.window, 0);
|
||||
isFullscreen = false;
|
||||
}
|
||||
|
||||
bool SDL2DisplayWindow::IsWindowFullscreen()
|
||||
{
|
||||
return isFullscreen;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Hide()
|
||||
{
|
||||
SDL_HideWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Activate()
|
||||
{
|
||||
SDL_RaiseWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowCursor(bool enable)
|
||||
{
|
||||
SDL_ShowCursor(enable);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::LockCursor()
|
||||
{
|
||||
if (!CursorLocked)
|
||||
{
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
CursorLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::UnlockCursor()
|
||||
{
|
||||
if (CursorLocked)
|
||||
{
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
CursorLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::CaptureMouse()
|
||||
{
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ReleaseMouseCapture()
|
||||
{
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetCursor(StandardCursor cursor)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Update()
|
||||
{
|
||||
SDL_Event event = {};
|
||||
event.type = PaintEventNumber;
|
||||
event.user.windowID = SDL_GetWindowID(Handle.window);
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
bool SDL2DisplayWindow::GetKeyState(InputKey key)
|
||||
{
|
||||
int numkeys = 0;
|
||||
const Uint8* state = SDL_GetKeyboardState(&numkeys);
|
||||
if (!state) return false;
|
||||
|
||||
SDL_Scancode index = InputKeyToScancode(key);
|
||||
return (index < numkeys) ? state[index] != 0 : false;
|
||||
}
|
||||
|
||||
Rect SDL2DisplayWindow::GetWindowFrame() const
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowPosition(Handle.window, &x, &y);
|
||||
SDL_GetWindowSize(Handle.window, &w, &h);
|
||||
return Rect::xywh(x / uiscale, y / uiscale, w / uiscale, h / uiscale);
|
||||
}
|
||||
|
||||
Point SDL2DisplayWindow::MapFromGlobal(const Point& pos) const
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowPosition(Handle.window, &x, &y);
|
||||
return Point(pos.x - x / uiscale, pos.y - y / uiscale);
|
||||
}
|
||||
|
||||
Point SDL2DisplayWindow::MapToGlobal(const Point& pos) const
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowPosition(Handle.window, &x, &y);
|
||||
return Point(pos.x + x / uiscale, pos.y + y / uiscale);
|
||||
}
|
||||
|
||||
Size SDL2DisplayWindow::GetClientSize() const
|
||||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowSize(Handle.window, &w, &h);
|
||||
return Size(w / uiscale, h / uiscale);
|
||||
}
|
||||
|
||||
int SDL2DisplayWindow::GetPixelWidth() const
|
||||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
if (RendererHandle)
|
||||
SDL_GetRendererOutputSize(RendererHandle, &w, &h);
|
||||
else
|
||||
SDL_GL_GetDrawableSize(Handle.window, &w, &h);
|
||||
return w;
|
||||
}
|
||||
|
||||
int SDL2DisplayWindow::GetPixelHeight() const
|
||||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
if (RendererHandle)
|
||||
SDL_GetRendererOutputSize(RendererHandle, &w, &h);
|
||||
else
|
||||
SDL_GL_GetDrawableSize(Handle.window, &w, &h);
|
||||
return h;
|
||||
}
|
||||
|
||||
double SDL2DisplayWindow::GetDpiScale() const
|
||||
{
|
||||
// SDL2 doesn't really support this properly. SDL_GetDisplayDPI returns the wrong information according to the docs.
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::PresentBitmap(int width, int height, const uint32_t* pixels)
|
||||
{
|
||||
if (!RendererHandle)
|
||||
return;
|
||||
|
||||
if (!BackBufferTexture || BackBufferWidth != width || BackBufferHeight != height)
|
||||
{
|
||||
if (BackBufferTexture)
|
||||
{
|
||||
SDL_DestroyTexture(BackBufferTexture);
|
||||
BackBufferTexture = nullptr;
|
||||
}
|
||||
|
||||
BackBufferTexture = SDL_CreateTexture(RendererHandle, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
if (!BackBufferTexture)
|
||||
return;
|
||||
|
||||
BackBufferWidth = width;
|
||||
BackBufferHeight = height;
|
||||
}
|
||||
|
||||
int destpitch = 0;
|
||||
void* dest = nullptr;
|
||||
int result = SDL_LockTexture(BackBufferTexture, nullptr, &dest, &destpitch);
|
||||
if (result != 0) return;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
const void* sline = pixels + y * width;
|
||||
void* dline = (uint8_t*)dest + y * destpitch;
|
||||
memcpy(dline, sline, width << 2);
|
||||
}
|
||||
SDL_UnlockTexture(BackBufferTexture);
|
||||
|
||||
SDL_RenderCopy(RendererHandle, BackBufferTexture, nullptr, nullptr);
|
||||
SDL_RenderPresent(RendererHandle);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetBorderColor(uint32_t bgra8)
|
||||
{
|
||||
// SDL doesn't have this
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetCaptionColor(uint32_t bgra8)
|
||||
{
|
||||
// SDL doesn't have this
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetCaptionTextColor(uint32_t bgra8)
|
||||
{
|
||||
// SDL doesn't have this
|
||||
}
|
||||
|
||||
std::string SDL2DisplayWindow::GetClipboardText()
|
||||
{
|
||||
char* buffer = SDL_GetClipboardText();
|
||||
if (!buffer)
|
||||
return {};
|
||||
std::string text = buffer;
|
||||
SDL_free(buffer);
|
||||
return text;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetClipboardText(const std::string& text)
|
||||
{
|
||||
SDL_SetClipboardText(text.c_str());
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ProcessEvents()
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event) != 0)
|
||||
{
|
||||
DispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::RunLoop()
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
ExitRunLoop = false;
|
||||
while (!ExitRunLoop)
|
||||
{
|
||||
SDL_Event event = {};
|
||||
int result = SDL_WaitEvent(&event);
|
||||
if (result == 1)
|
||||
DispatchEvent(event); // Silently ignore if it fails and pray it doesn't busy loop, because SDL and Linux utterly sucks!
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ExitLoop()
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
ExitRunLoop = true;
|
||||
}
|
||||
|
||||
Size SDL2DisplayWindow::GetScreenSize()
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
SDL_Rect rect = {};
|
||||
int result = SDL_GetDisplayBounds(0, &rect);
|
||||
if (result != 0)
|
||||
throw std::runtime_error(std::string("Unable to get screen size:") + SDL_GetError());
|
||||
|
||||
double uiscale = 1.0; // SDL2 doesn't really support this properly. SDL_GetDisplayDPI returns the wrong information according to the docs.
|
||||
return Size(rect.w / uiscale, rect.h / uiscale);
|
||||
}
|
||||
|
||||
void* SDL2DisplayWindow::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
// To do: implement timers
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::StopTimer(void* timerID)
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
// To do: implement timers
|
||||
}
|
||||
|
||||
SDL2DisplayWindow* SDL2DisplayWindow::FindEventWindow(const SDL_Event& event)
|
||||
{
|
||||
int windowID;
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_WINDOWEVENT: windowID = event.window.windowID; break;
|
||||
case SDL_TEXTINPUT: windowID = event.text.windowID; break;
|
||||
case SDL_KEYUP: windowID = event.key.windowID; break;
|
||||
case SDL_KEYDOWN: windowID = event.key.windowID; break;
|
||||
case SDL_MOUSEBUTTONUP: windowID = event.button.windowID; break;
|
||||
case SDL_MOUSEBUTTONDOWN: windowID = event.button.windowID; break;
|
||||
case SDL_MOUSEWHEEL: windowID = event.wheel.windowID; break;
|
||||
case SDL_MOUSEMOTION: windowID = event.motion.windowID; break;
|
||||
default:
|
||||
if (event.type == PaintEventNumber) windowID = event.user.windowID;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
auto it = WindowList.find(windowID);
|
||||
return it != WindowList.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::DispatchEvent(const SDL_Event& event)
|
||||
{
|
||||
SDL2DisplayWindow* window = FindEventWindow(event);
|
||||
if (!window) return;
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_WINDOWEVENT: window->OnWindowEvent(event.window); break;
|
||||
case SDL_TEXTINPUT: window->OnTextInput(event.text); break;
|
||||
case SDL_KEYUP: window->OnKeyUp(event.key); break;
|
||||
case SDL_KEYDOWN: window->OnKeyDown(event.key); break;
|
||||
case SDL_MOUSEBUTTONUP: window->OnMouseButtonUp(event.button); break;
|
||||
case SDL_MOUSEBUTTONDOWN: window->OnMouseButtonDown(event.button); break;
|
||||
case SDL_MOUSEWHEEL: window->OnMouseWheel(event.wheel); break;
|
||||
case SDL_MOUSEMOTION: window->OnMouseMotion(event.motion); break;
|
||||
default:
|
||||
if (event.type == PaintEventNumber) window->OnPaintEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnWindowEvent(const SDL_WindowEvent& event)
|
||||
{
|
||||
switch (event.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_CLOSE: WindowHost->OnWindowClose(); break;
|
||||
case SDL_WINDOWEVENT_MOVED: WindowHost->OnWindowGeometryChanged(); break;
|
||||
case SDL_WINDOWEVENT_RESIZED: WindowHost->OnWindowGeometryChanged(); break;
|
||||
case SDL_WINDOWEVENT_SHOWN: WindowHost->OnWindowPaint(); break;
|
||||
case SDL_WINDOWEVENT_EXPOSED: WindowHost->OnWindowPaint(); break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED: WindowHost->OnWindowActivated(); break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST: WindowHost->OnWindowDeactivated(); break;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnTextInput(const SDL_TextInputEvent& event)
|
||||
{
|
||||
WindowHost->OnWindowKeyChar(event.text);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnKeyUp(const SDL_KeyboardEvent& event)
|
||||
{
|
||||
WindowHost->OnWindowKeyUp(ScancodeToInputKey(event.keysym.scancode));
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnKeyDown(const SDL_KeyboardEvent& event)
|
||||
{
|
||||
WindowHost->OnWindowKeyDown(ScancodeToInputKey(event.keysym.scancode));
|
||||
}
|
||||
|
||||
InputKey SDL2DisplayWindow::GetMouseButtonKey(const SDL_MouseButtonEvent& event)
|
||||
{
|
||||
switch (event.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: return InputKey::LeftMouse;
|
||||
case SDL_BUTTON_MIDDLE: return InputKey::MiddleMouse;
|
||||
case SDL_BUTTON_RIGHT: return InputKey::RightMouse;
|
||||
// case SDL_BUTTON_X1: return InputKey::XButton1;
|
||||
// case SDL_BUTTON_X2: return InputKey::XButton2;
|
||||
default: return InputKey::None;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnMouseButtonUp(const SDL_MouseButtonEvent& event)
|
||||
{
|
||||
InputKey key = GetMouseButtonKey(event);
|
||||
if (key != InputKey::None)
|
||||
{
|
||||
WindowHost->OnWindowMouseUp(GetMousePos(event), key);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnMouseButtonDown(const SDL_MouseButtonEvent& event)
|
||||
{
|
||||
InputKey key = GetMouseButtonKey(event);
|
||||
if (key != InputKey::None)
|
||||
{
|
||||
WindowHost->OnWindowMouseDown(GetMousePos(event), key);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnMouseWheel(const SDL_MouseWheelEvent& event)
|
||||
{
|
||||
InputKey key = (event.y > 0) ? InputKey::MouseWheelUp : (event.y < 0) ? InputKey::MouseWheelDown : InputKey::None;
|
||||
if (key != InputKey::None)
|
||||
{
|
||||
WindowHost->OnWindowMouseWheel(GetMousePos(event), key);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnMouseMotion(const SDL_MouseMotionEvent& event)
|
||||
{
|
||||
if (CursorLocked)
|
||||
{
|
||||
WindowHost->OnWindowRawMouseMove(event.xrel, event.yrel);
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowHost->OnWindowMouseMove(GetMousePos(event));
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnPaintEvent()
|
||||
{
|
||||
WindowHost->OnWindowPaint();
|
||||
}
|
||||
|
||||
InputKey SDL2DisplayWindow::ScancodeToInputKey(SDL_Scancode keycode)
|
||||
{
|
||||
switch (keycode)
|
||||
{
|
||||
case SDL_SCANCODE_BACKSPACE: return InputKey::Backspace;
|
||||
case SDL_SCANCODE_TAB: return InputKey::Tab;
|
||||
case SDL_SCANCODE_CLEAR: return InputKey::OEMClear;
|
||||
case SDL_SCANCODE_RETURN: return InputKey::Enter;
|
||||
case SDL_SCANCODE_MENU: return InputKey::Alt;
|
||||
case SDL_SCANCODE_PAUSE: return InputKey::Pause;
|
||||
case SDL_SCANCODE_ESCAPE: return InputKey::Escape;
|
||||
case SDL_SCANCODE_SPACE: return InputKey::Space;
|
||||
case SDL_SCANCODE_END: return InputKey::End;
|
||||
case SDL_SCANCODE_HOME: return InputKey::Home;
|
||||
case SDL_SCANCODE_LEFT: return InputKey::Left;
|
||||
case SDL_SCANCODE_UP: return InputKey::Up;
|
||||
case SDL_SCANCODE_RIGHT: return InputKey::Right;
|
||||
case SDL_SCANCODE_DOWN: return InputKey::Down;
|
||||
case SDL_SCANCODE_SELECT: return InputKey::Select;
|
||||
case SDL_SCANCODE_PRINTSCREEN: return InputKey::Print;
|
||||
case SDL_SCANCODE_EXECUTE: return InputKey::Execute;
|
||||
case SDL_SCANCODE_INSERT: return InputKey::Insert;
|
||||
case SDL_SCANCODE_DELETE: return InputKey::Delete;
|
||||
case SDL_SCANCODE_HELP: return InputKey::Help;
|
||||
case SDL_SCANCODE_0: return InputKey::_0;
|
||||
case SDL_SCANCODE_1: return InputKey::_1;
|
||||
case SDL_SCANCODE_2: return InputKey::_2;
|
||||
case SDL_SCANCODE_3: return InputKey::_3;
|
||||
case SDL_SCANCODE_4: return InputKey::_4;
|
||||
case SDL_SCANCODE_5: return InputKey::_5;
|
||||
case SDL_SCANCODE_6: return InputKey::_6;
|
||||
case SDL_SCANCODE_7: return InputKey::_7;
|
||||
case SDL_SCANCODE_8: return InputKey::_8;
|
||||
case SDL_SCANCODE_9: return InputKey::_9;
|
||||
case SDL_SCANCODE_A: return InputKey::A;
|
||||
case SDL_SCANCODE_B: return InputKey::B;
|
||||
case SDL_SCANCODE_C: return InputKey::C;
|
||||
case SDL_SCANCODE_D: return InputKey::D;
|
||||
case SDL_SCANCODE_E: return InputKey::E;
|
||||
case SDL_SCANCODE_F: return InputKey::F;
|
||||
case SDL_SCANCODE_G: return InputKey::G;
|
||||
case SDL_SCANCODE_H: return InputKey::H;
|
||||
case SDL_SCANCODE_I: return InputKey::I;
|
||||
case SDL_SCANCODE_J: return InputKey::J;
|
||||
case SDL_SCANCODE_K: return InputKey::K;
|
||||
case SDL_SCANCODE_L: return InputKey::L;
|
||||
case SDL_SCANCODE_M: return InputKey::M;
|
||||
case SDL_SCANCODE_N: return InputKey::N;
|
||||
case SDL_SCANCODE_O: return InputKey::O;
|
||||
case SDL_SCANCODE_P: return InputKey::P;
|
||||
case SDL_SCANCODE_Q: return InputKey::Q;
|
||||
case SDL_SCANCODE_R: return InputKey::R;
|
||||
case SDL_SCANCODE_S: return InputKey::S;
|
||||
case SDL_SCANCODE_T: return InputKey::T;
|
||||
case SDL_SCANCODE_U: return InputKey::U;
|
||||
case SDL_SCANCODE_V: return InputKey::V;
|
||||
case SDL_SCANCODE_W: return InputKey::W;
|
||||
case SDL_SCANCODE_X: return InputKey::X;
|
||||
case SDL_SCANCODE_Y: return InputKey::Y;
|
||||
case SDL_SCANCODE_Z: return InputKey::Z;
|
||||
case SDL_SCANCODE_KP_0: return InputKey::NumPad0;
|
||||
case SDL_SCANCODE_KP_1: return InputKey::NumPad1;
|
||||
case SDL_SCANCODE_KP_2: return InputKey::NumPad2;
|
||||
case SDL_SCANCODE_KP_3: return InputKey::NumPad3;
|
||||
case SDL_SCANCODE_KP_4: return InputKey::NumPad4;
|
||||
case SDL_SCANCODE_KP_5: return InputKey::NumPad5;
|
||||
case SDL_SCANCODE_KP_6: return InputKey::NumPad6;
|
||||
case SDL_SCANCODE_KP_7: return InputKey::NumPad7;
|
||||
case SDL_SCANCODE_KP_8: return InputKey::NumPad8;
|
||||
case SDL_SCANCODE_KP_9: return InputKey::NumPad9;
|
||||
// case SDL_SCANCODE_KP_ENTER: return InputKey::NumPadEnter;
|
||||
// case SDL_SCANCODE_KP_MULTIPLY: return InputKey::Multiply;
|
||||
// case SDL_SCANCODE_KP_PLUS: return InputKey::Add;
|
||||
case SDL_SCANCODE_SEPARATOR: return InputKey::Separator;
|
||||
// case SDL_SCANCODE_KP_MINUS: return InputKey::Subtract;
|
||||
case SDL_SCANCODE_KP_PERIOD: return InputKey::NumPadPeriod;
|
||||
// case SDL_SCANCODE_KP_DIVIDE: return InputKey::Divide;
|
||||
case SDL_SCANCODE_F1: return InputKey::F1;
|
||||
case SDL_SCANCODE_F2: return InputKey::F2;
|
||||
case SDL_SCANCODE_F3: return InputKey::F3;
|
||||
case SDL_SCANCODE_F4: return InputKey::F4;
|
||||
case SDL_SCANCODE_F5: return InputKey::F5;
|
||||
case SDL_SCANCODE_F6: return InputKey::F6;
|
||||
case SDL_SCANCODE_F7: return InputKey::F7;
|
||||
case SDL_SCANCODE_F8: return InputKey::F8;
|
||||
case SDL_SCANCODE_F9: return InputKey::F9;
|
||||
case SDL_SCANCODE_F10: return InputKey::F10;
|
||||
case SDL_SCANCODE_F11: return InputKey::F11;
|
||||
case SDL_SCANCODE_F12: return InputKey::F12;
|
||||
case SDL_SCANCODE_F13: return InputKey::F13;
|
||||
case SDL_SCANCODE_F14: return InputKey::F14;
|
||||
case SDL_SCANCODE_F15: return InputKey::F15;
|
||||
case SDL_SCANCODE_F16: return InputKey::F16;
|
||||
case SDL_SCANCODE_F17: return InputKey::F17;
|
||||
case SDL_SCANCODE_F18: return InputKey::F18;
|
||||
case SDL_SCANCODE_F19: return InputKey::F19;
|
||||
case SDL_SCANCODE_F20: return InputKey::F20;
|
||||
case SDL_SCANCODE_F21: return InputKey::F21;
|
||||
case SDL_SCANCODE_F22: return InputKey::F22;
|
||||
case SDL_SCANCODE_F23: return InputKey::F23;
|
||||
case SDL_SCANCODE_F24: return InputKey::F24;
|
||||
case SDL_SCANCODE_NUMLOCKCLEAR: return InputKey::NumLock;
|
||||
case SDL_SCANCODE_SCROLLLOCK: return InputKey::ScrollLock;
|
||||
case SDL_SCANCODE_LSHIFT: return InputKey::LShift;
|
||||
case SDL_SCANCODE_RSHIFT: return InputKey::RShift;
|
||||
case SDL_SCANCODE_LCTRL: return InputKey::LControl;
|
||||
case SDL_SCANCODE_RCTRL: return InputKey::RControl;
|
||||
case SDL_SCANCODE_GRAVE: return InputKey::Tilde;
|
||||
default: return InputKey::None;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Scancode SDL2DisplayWindow::InputKeyToScancode(InputKey inputkey)
|
||||
{
|
||||
switch (inputkey)
|
||||
{
|
||||
case InputKey::Backspace: return SDL_SCANCODE_BACKSPACE;
|
||||
case InputKey::Tab: return SDL_SCANCODE_TAB;
|
||||
case InputKey::OEMClear: return SDL_SCANCODE_CLEAR;
|
||||
case InputKey::Enter: return SDL_SCANCODE_RETURN;
|
||||
case InputKey::Alt: return SDL_SCANCODE_MENU;
|
||||
case InputKey::Pause: return SDL_SCANCODE_PAUSE;
|
||||
case InputKey::Escape: return SDL_SCANCODE_ESCAPE;
|
||||
case InputKey::Space: return SDL_SCANCODE_SPACE;
|
||||
case InputKey::End: return SDL_SCANCODE_END;
|
||||
case InputKey::Home: return SDL_SCANCODE_HOME;
|
||||
case InputKey::Left: return SDL_SCANCODE_LEFT;
|
||||
case InputKey::Up: return SDL_SCANCODE_UP;
|
||||
case InputKey::Right: return SDL_SCANCODE_RIGHT;
|
||||
case InputKey::Down: return SDL_SCANCODE_DOWN;
|
||||
case InputKey::Select: return SDL_SCANCODE_SELECT;
|
||||
case InputKey::Print: return SDL_SCANCODE_PRINTSCREEN;
|
||||
case InputKey::Execute: return SDL_SCANCODE_EXECUTE;
|
||||
case InputKey::Insert: return SDL_SCANCODE_INSERT;
|
||||
case InputKey::Delete: return SDL_SCANCODE_DELETE;
|
||||
case InputKey::Help: return SDL_SCANCODE_HELP;
|
||||
case InputKey::_0: return SDL_SCANCODE_0;
|
||||
case InputKey::_1: return SDL_SCANCODE_1;
|
||||
case InputKey::_2: return SDL_SCANCODE_2;
|
||||
case InputKey::_3: return SDL_SCANCODE_3;
|
||||
case InputKey::_4: return SDL_SCANCODE_4;
|
||||
case InputKey::_5: return SDL_SCANCODE_5;
|
||||
case InputKey::_6: return SDL_SCANCODE_6;
|
||||
case InputKey::_7: return SDL_SCANCODE_7;
|
||||
case InputKey::_8: return SDL_SCANCODE_8;
|
||||
case InputKey::_9: return SDL_SCANCODE_9;
|
||||
case InputKey::A: return SDL_SCANCODE_A;
|
||||
case InputKey::B: return SDL_SCANCODE_B;
|
||||
case InputKey::C: return SDL_SCANCODE_C;
|
||||
case InputKey::D: return SDL_SCANCODE_D;
|
||||
case InputKey::E: return SDL_SCANCODE_E;
|
||||
case InputKey::F: return SDL_SCANCODE_F;
|
||||
case InputKey::G: return SDL_SCANCODE_G;
|
||||
case InputKey::H: return SDL_SCANCODE_H;
|
||||
case InputKey::I: return SDL_SCANCODE_I;
|
||||
case InputKey::J: return SDL_SCANCODE_J;
|
||||
case InputKey::K: return SDL_SCANCODE_K;
|
||||
case InputKey::L: return SDL_SCANCODE_L;
|
||||
case InputKey::M: return SDL_SCANCODE_M;
|
||||
case InputKey::N: return SDL_SCANCODE_N;
|
||||
case InputKey::O: return SDL_SCANCODE_O;
|
||||
case InputKey::P: return SDL_SCANCODE_P;
|
||||
case InputKey::Q: return SDL_SCANCODE_Q;
|
||||
case InputKey::R: return SDL_SCANCODE_R;
|
||||
case InputKey::S: return SDL_SCANCODE_S;
|
||||
case InputKey::T: return SDL_SCANCODE_T;
|
||||
case InputKey::U: return SDL_SCANCODE_U;
|
||||
case InputKey::V: return SDL_SCANCODE_V;
|
||||
case InputKey::W: return SDL_SCANCODE_W;
|
||||
case InputKey::X: return SDL_SCANCODE_X;
|
||||
case InputKey::Y: return SDL_SCANCODE_Y;
|
||||
case InputKey::Z: return SDL_SCANCODE_Z;
|
||||
case InputKey::NumPad0: return SDL_SCANCODE_KP_0;
|
||||
case InputKey::NumPad1: return SDL_SCANCODE_KP_1;
|
||||
case InputKey::NumPad2: return SDL_SCANCODE_KP_2;
|
||||
case InputKey::NumPad3: return SDL_SCANCODE_KP_3;
|
||||
case InputKey::NumPad4: return SDL_SCANCODE_KP_4;
|
||||
case InputKey::NumPad5: return SDL_SCANCODE_KP_5;
|
||||
case InputKey::NumPad6: return SDL_SCANCODE_KP_6;
|
||||
case InputKey::NumPad7: return SDL_SCANCODE_KP_7;
|
||||
case InputKey::NumPad8: return SDL_SCANCODE_KP_8;
|
||||
case InputKey::NumPad9: return SDL_SCANCODE_KP_9;
|
||||
// case InputKey::NumPadEnter: return SDL_SCANCODE_KP_ENTER;
|
||||
// case InputKey::Multiply return SDL_SCANCODE_KP_MULTIPLY:;
|
||||
// case InputKey::Add: return SDL_SCANCODE_KP_PLUS;
|
||||
case InputKey::Separator: return SDL_SCANCODE_SEPARATOR;
|
||||
// case InputKey::Subtract: return SDL_SCANCODE_KP_MINUS;
|
||||
case InputKey::NumPadPeriod: return SDL_SCANCODE_KP_PERIOD;
|
||||
// case InputKey::Divide: return SDL_SCANCODE_KP_DIVIDE;
|
||||
case InputKey::F1: return SDL_SCANCODE_F1;
|
||||
case InputKey::F2: return SDL_SCANCODE_F2;
|
||||
case InputKey::F3: return SDL_SCANCODE_F3;
|
||||
case InputKey::F4: return SDL_SCANCODE_F4;
|
||||
case InputKey::F5: return SDL_SCANCODE_F5;
|
||||
case InputKey::F6: return SDL_SCANCODE_F6;
|
||||
case InputKey::F7: return SDL_SCANCODE_F7;
|
||||
case InputKey::F8: return SDL_SCANCODE_F8;
|
||||
case InputKey::F9: return SDL_SCANCODE_F9;
|
||||
case InputKey::F10: return SDL_SCANCODE_F10;
|
||||
case InputKey::F11: return SDL_SCANCODE_F11;
|
||||
case InputKey::F12: return SDL_SCANCODE_F12;
|
||||
case InputKey::F13: return SDL_SCANCODE_F13;
|
||||
case InputKey::F14: return SDL_SCANCODE_F14;
|
||||
case InputKey::F15: return SDL_SCANCODE_F15;
|
||||
case InputKey::F16: return SDL_SCANCODE_F16;
|
||||
case InputKey::F17: return SDL_SCANCODE_F17;
|
||||
case InputKey::F18: return SDL_SCANCODE_F18;
|
||||
case InputKey::F19: return SDL_SCANCODE_F19;
|
||||
case InputKey::F20: return SDL_SCANCODE_F20;
|
||||
case InputKey::F21: return SDL_SCANCODE_F21;
|
||||
case InputKey::F22: return SDL_SCANCODE_F22;
|
||||
case InputKey::F23: return SDL_SCANCODE_F23;
|
||||
case InputKey::F24: return SDL_SCANCODE_F24;
|
||||
case InputKey::NumLock: return SDL_SCANCODE_NUMLOCKCLEAR;
|
||||
case InputKey::ScrollLock: return SDL_SCANCODE_SCROLLLOCK;
|
||||
case InputKey::LShift: return SDL_SCANCODE_LSHIFT;
|
||||
case InputKey::RShift: return SDL_SCANCODE_RSHIFT;
|
||||
case InputKey::LControl: return SDL_SCANCODE_LCTRL;
|
||||
case InputKey::RControl: return SDL_SCANCODE_RCTRL;
|
||||
case InputKey::Tilde: return SDL_SCANCODE_GRAVE;
|
||||
default: return (SDL_Scancode)0;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,13 @@
|
|||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <zwidget/window/window.h>
|
||||
#include <zwidget/window/sdl2nativehandle.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
class SDL2DisplayWindow : public DisplayWindow
|
||||
{
|
||||
public:
|
||||
SDL2DisplayWindow(DisplayWindowHost* windowHost);
|
||||
SDL2DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, SDL2DisplayWindow* owner, RenderAPI renderAPI);
|
||||
~SDL2DisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
|
|
@ -19,6 +20,7 @@ public:
|
|||
void ShowMaximized() override;
|
||||
void ShowMinimized() override;
|
||||
void ShowNormal() override;
|
||||
bool IsWindowFullscreen() override;
|
||||
void Hide() override;
|
||||
void Activate() override;
|
||||
void ShowCursor(bool enable) override;
|
||||
|
|
@ -27,7 +29,7 @@ public:
|
|||
void CaptureMouse() override;
|
||||
void ReleaseMouseCapture() override;
|
||||
void Update() override;
|
||||
bool GetKeyState(EInputKey key) override;
|
||||
bool GetKeyState(InputKey key) override;
|
||||
void SetCursor(StandardCursor cursor) override;
|
||||
|
||||
Rect GetWindowFrame() const override;
|
||||
|
|
@ -45,6 +47,14 @@ public:
|
|||
std::string GetClipboardText() override;
|
||||
void SetClipboardText(const std::string& text) override;
|
||||
|
||||
Point MapFromGlobal(const Point& pos) const override;
|
||||
Point MapToGlobal(const Point& pos) const override;
|
||||
|
||||
void* GetNativeHandle() override { return &Handle; }
|
||||
|
||||
std::vector<std::string> GetVulkanInstanceExtensions() override;
|
||||
VkSurfaceKHR CreateVulkanSurface(VkInstance instance) override;
|
||||
|
||||
static void DispatchEvent(const SDL_Event& event);
|
||||
static SDL2DisplayWindow* FindEventWindow(const SDL_Event& event);
|
||||
|
||||
|
|
@ -58,10 +68,10 @@ public:
|
|||
void OnMouseMotion(const SDL_MouseMotionEvent& event);
|
||||
void OnPaintEvent();
|
||||
|
||||
EInputKey GetMouseButtonKey(const SDL_MouseButtonEvent& event);
|
||||
InputKey GetMouseButtonKey(const SDL_MouseButtonEvent& event);
|
||||
|
||||
static EInputKey ScancodeToInputKey(SDL_Scancode keycode);
|
||||
static SDL_Scancode InputKeyToScancode(EInputKey inputkey);
|
||||
static InputKey ScancodeToInputKey(SDL_Scancode keycode);
|
||||
static SDL_Scancode InputKeyToScancode(InputKey inputkey);
|
||||
|
||||
template<typename T>
|
||||
Point GetMousePos(const T& event)
|
||||
|
|
@ -79,12 +89,15 @@ public:
|
|||
static void StopTimer(void* timerID);
|
||||
|
||||
DisplayWindowHost* WindowHost = nullptr;
|
||||
SDL_Window* WindowHandle = nullptr;
|
||||
SDL2NativeHandle Handle;
|
||||
SDL_Renderer* RendererHandle = nullptr;
|
||||
SDL_Texture* BackBufferTexture = nullptr;
|
||||
int BackBufferWidth = 0;
|
||||
int BackBufferHeight = 0;
|
||||
|
||||
bool CursorLocked = false;
|
||||
bool isFullscreen = false;
|
||||
|
||||
static bool ExitRunLoop;
|
||||
static Uint32 PaintEventNumber;
|
||||
static std::unordered_map<int, SDL2DisplayWindow*> WindowList;
|
||||
|
|
@ -1,675 +0,0 @@
|
|||
|
||||
#include "sdl2displaywindow.h"
|
||||
#include <stdexcept>
|
||||
|
||||
Uint32 SDL2DisplayWindow::PaintEventNumber = 0xffffffff;
|
||||
bool SDL2DisplayWindow::ExitRunLoop;
|
||||
std::unordered_map<int, SDL2DisplayWindow*> SDL2DisplayWindow::WindowList;
|
||||
|
||||
class InitSDL
|
||||
{
|
||||
public:
|
||||
InitSDL()
|
||||
{
|
||||
int result = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
|
||||
if (result != 0)
|
||||
throw std::runtime_error(std::string("Unable to initialize SDL:") + SDL_GetError());
|
||||
|
||||
SDL2DisplayWindow::PaintEventNumber = SDL_RegisterEvents(1);
|
||||
}
|
||||
};
|
||||
|
||||
static void CheckInitSDL()
|
||||
{
|
||||
static InitSDL initsdl;
|
||||
}
|
||||
|
||||
SDL2DisplayWindow::SDL2DisplayWindow(DisplayWindowHost* windowHost) : WindowHost(windowHost)
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
int result = SDL_CreateWindowAndRenderer(320, 200, SDL_WINDOW_HIDDEN /*| SDL_WINDOW_ALLOW_HIGHDPI*/, &WindowHandle, &RendererHandle);
|
||||
if (result != 0)
|
||||
throw std::runtime_error(std::string("Unable to create SDL window:") + SDL_GetError());
|
||||
|
||||
WindowList[SDL_GetWindowID(WindowHandle)] = this;
|
||||
}
|
||||
|
||||
SDL2DisplayWindow::~SDL2DisplayWindow()
|
||||
{
|
||||
WindowList.erase(WindowList.find(SDL_GetWindowID(WindowHandle)));
|
||||
|
||||
if (BackBufferTexture)
|
||||
{
|
||||
SDL_DestroyTexture(BackBufferTexture);
|
||||
BackBufferTexture = nullptr;
|
||||
}
|
||||
|
||||
SDL_DestroyRenderer(RendererHandle);
|
||||
SDL_DestroyWindow(WindowHandle);
|
||||
RendererHandle = nullptr;
|
||||
WindowHandle = nullptr;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetWindowTitle(const std::string& text)
|
||||
{
|
||||
SDL_SetWindowTitle(WindowHandle, text.c_str());
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetWindowFrame(const Rect& box)
|
||||
{
|
||||
// SDL2 doesn't really seem to have an API for this.
|
||||
// The docs aren't clear what you're setting when calling SDL_SetWindowSize.
|
||||
SetClientFrame(box);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetClientFrame(const Rect& box)
|
||||
{
|
||||
// Is there a way to set both in one call?
|
||||
|
||||
double uiscale = GetDpiScale();
|
||||
int x = (int)std::round(box.x * uiscale);
|
||||
int y = (int)std::round(box.y * uiscale);
|
||||
int w = (int)std::round(box.width * uiscale);
|
||||
int h = (int)std::round(box.height * uiscale);
|
||||
|
||||
SDL_SetWindowPosition(WindowHandle, x, y);
|
||||
SDL_SetWindowSize(WindowHandle, w, h);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Show()
|
||||
{
|
||||
SDL_ShowWindow(WindowHandle);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowFullscreen()
|
||||
{
|
||||
SDL_SetWindowFullscreen(WindowHandle, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowMaximized()
|
||||
{
|
||||
SDL_ShowWindow(WindowHandle);
|
||||
SDL_MaximizeWindow(WindowHandle);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowMinimized()
|
||||
{
|
||||
SDL_ShowWindow(WindowHandle);
|
||||
SDL_MinimizeWindow(WindowHandle);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowNormal()
|
||||
{
|
||||
SDL_ShowWindow(WindowHandle);
|
||||
SDL_SetWindowFullscreen(WindowHandle, 0);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Hide()
|
||||
{
|
||||
SDL_HideWindow(WindowHandle);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Activate()
|
||||
{
|
||||
SDL_RaiseWindow(WindowHandle);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowCursor(bool enable)
|
||||
{
|
||||
SDL_ShowCursor(enable);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::LockCursor()
|
||||
{
|
||||
SDL_SetWindowGrab(WindowHandle, SDL_TRUE);
|
||||
SDL_ShowCursor(0);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::UnlockCursor()
|
||||
{
|
||||
SDL_SetWindowGrab(WindowHandle, SDL_FALSE);
|
||||
SDL_ShowCursor(1);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::CaptureMouse()
|
||||
{
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ReleaseMouseCapture()
|
||||
{
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetCursor(StandardCursor cursor)
|
||||
{
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Update()
|
||||
{
|
||||
SDL_Event event = {};
|
||||
event.type = PaintEventNumber;
|
||||
event.user.windowID = SDL_GetWindowID(WindowHandle);
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
bool SDL2DisplayWindow::GetKeyState(EInputKey key)
|
||||
{
|
||||
int numkeys = 0;
|
||||
const Uint8* state = SDL_GetKeyboardState(&numkeys);
|
||||
if (!state) return false;
|
||||
|
||||
SDL_Scancode index = InputKeyToScancode(key);
|
||||
return (index < numkeys) ? state[index] != 0 : false;
|
||||
}
|
||||
|
||||
Rect SDL2DisplayWindow::GetWindowFrame() const
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowPosition(WindowHandle, &x, &y);
|
||||
SDL_GetWindowSize(WindowHandle, &w, &h);
|
||||
return Rect::xywh(x / uiscale, y / uiscale, w / uiscale, h / uiscale);
|
||||
}
|
||||
|
||||
Size SDL2DisplayWindow::GetClientSize() const
|
||||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowSize(WindowHandle, &w, &h);
|
||||
return Size(w / uiscale, h / uiscale);
|
||||
}
|
||||
|
||||
int SDL2DisplayWindow::GetPixelWidth() const
|
||||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int result = SDL_GetRendererOutputSize(RendererHandle, &w, &h);
|
||||
return w;
|
||||
}
|
||||
|
||||
int SDL2DisplayWindow::GetPixelHeight() const
|
||||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int result = SDL_GetRendererOutputSize(RendererHandle, &w, &h);
|
||||
return h;
|
||||
}
|
||||
|
||||
double SDL2DisplayWindow::GetDpiScale() const
|
||||
{
|
||||
// SDL2 doesn't really support this properly. SDL_GetDisplayDPI returns the wrong information according to the docs.
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::PresentBitmap(int width, int height, const uint32_t* pixels)
|
||||
{
|
||||
if (!BackBufferTexture || BackBufferWidth != width || BackBufferHeight != height)
|
||||
{
|
||||
if (BackBufferTexture)
|
||||
{
|
||||
SDL_DestroyTexture(BackBufferTexture);
|
||||
BackBufferTexture = nullptr;
|
||||
}
|
||||
|
||||
BackBufferTexture = SDL_CreateTexture(RendererHandle, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
if (!BackBufferTexture)
|
||||
return;
|
||||
|
||||
BackBufferWidth = width;
|
||||
BackBufferHeight = height;
|
||||
}
|
||||
|
||||
int destpitch = 0;
|
||||
void* dest = nullptr;
|
||||
int result = SDL_LockTexture(BackBufferTexture, nullptr, &dest, &destpitch);
|
||||
if (result != 0) return;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
const void* sline = pixels + y * width;
|
||||
void* dline = (uint8_t*)dest + y * destpitch;
|
||||
memcpy(dline, sline, width << 2);
|
||||
}
|
||||
SDL_UnlockTexture(BackBufferTexture);
|
||||
|
||||
SDL_RenderCopy(RendererHandle, BackBufferTexture, nullptr, nullptr);
|
||||
SDL_RenderPresent(RendererHandle);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetBorderColor(uint32_t bgra8)
|
||||
{
|
||||
// SDL doesn't have this
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetCaptionColor(uint32_t bgra8)
|
||||
{
|
||||
// SDL doesn't have this
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetCaptionTextColor(uint32_t bgra8)
|
||||
{
|
||||
// SDL doesn't have this
|
||||
}
|
||||
|
||||
std::string SDL2DisplayWindow::GetClipboardText()
|
||||
{
|
||||
char* buffer = SDL_GetClipboardText();
|
||||
if (!buffer)
|
||||
return {};
|
||||
std::string text = buffer;
|
||||
SDL_free(buffer);
|
||||
return text;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetClipboardText(const std::string& text)
|
||||
{
|
||||
SDL_SetClipboardText(text.c_str());
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ProcessEvents()
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event) != 0)
|
||||
{
|
||||
DispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::RunLoop()
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
while (!ExitRunLoop)
|
||||
{
|
||||
SDL_Event event;
|
||||
int result = SDL_WaitEvent(&event);
|
||||
if (result == 1)
|
||||
DispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ExitLoop()
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
ExitRunLoop = true;
|
||||
}
|
||||
|
||||
Size SDL2DisplayWindow::GetScreenSize()
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
SDL_Rect rect = {};
|
||||
int result = SDL_GetDisplayBounds(0, &rect);
|
||||
if (result != 0)
|
||||
throw std::runtime_error(std::string("Unable to get screen size:") + SDL_GetError());
|
||||
|
||||
double uiscale = 1.0; // SDL2 doesn't really support this properly. SDL_GetDisplayDPI returns the wrong information according to the docs.
|
||||
return Size(rect.w / uiscale, rect.h / uiscale);
|
||||
}
|
||||
|
||||
void* SDL2DisplayWindow::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
// To do: implement timers
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::StopTimer(void* timerID)
|
||||
{
|
||||
CheckInitSDL();
|
||||
|
||||
// To do: implement timers
|
||||
}
|
||||
|
||||
SDL2DisplayWindow* SDL2DisplayWindow::FindEventWindow(const SDL_Event& event)
|
||||
{
|
||||
int windowID;
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_WINDOWEVENT: windowID = event.window.windowID; break;
|
||||
case SDL_TEXTINPUT: windowID = event.text.windowID; break;
|
||||
case SDL_KEYUP: windowID = event.key.windowID; break;
|
||||
case SDL_KEYDOWN: windowID = event.key.windowID; break;
|
||||
case SDL_MOUSEBUTTONUP: windowID = event.button.windowID; break;
|
||||
case SDL_MOUSEBUTTONDOWN: windowID = event.button.windowID; break;
|
||||
case SDL_MOUSEWHEEL: windowID = event.wheel.windowID; break;
|
||||
case SDL_MOUSEMOTION: windowID = event.motion.windowID; break;
|
||||
default:
|
||||
if (event.type == PaintEventNumber) windowID = event.user.windowID;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
auto it = WindowList.find(windowID);
|
||||
return it != WindowList.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::DispatchEvent(const SDL_Event& event)
|
||||
{
|
||||
SDL2DisplayWindow* window = FindEventWindow(event);
|
||||
if (!window) return;
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_WINDOWEVENT: window->OnWindowEvent(event.window); break;
|
||||
case SDL_TEXTINPUT: window->OnTextInput(event.text); break;
|
||||
case SDL_KEYUP: window->OnKeyUp(event.key); break;
|
||||
case SDL_KEYDOWN: window->OnKeyDown(event.key); break;
|
||||
case SDL_MOUSEBUTTONUP: window->OnMouseButtonUp(event.button); break;
|
||||
case SDL_MOUSEBUTTONDOWN: window->OnMouseButtonDown(event.button); break;
|
||||
case SDL_MOUSEWHEEL: window->OnMouseWheel(event.wheel); break;
|
||||
case SDL_MOUSEMOTION: window->OnMouseMotion(event.motion); break;
|
||||
default:
|
||||
if (event.type == PaintEventNumber) window->OnPaintEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnWindowEvent(const SDL_WindowEvent& event)
|
||||
{
|
||||
switch (event.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_CLOSE: WindowHost->OnWindowClose(); break;
|
||||
case SDL_WINDOWEVENT_MOVED: WindowHost->OnWindowGeometryChanged(); break;
|
||||
case SDL_WINDOWEVENT_RESIZED: WindowHost->OnWindowGeometryChanged(); break;
|
||||
case SDL_WINDOWEVENT_SHOWN: WindowHost->OnWindowPaint(); break;
|
||||
case SDL_WINDOWEVENT_EXPOSED: WindowHost->OnWindowPaint(); break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED: WindowHost->OnWindowActivated(); break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST: WindowHost->OnWindowDeactivated(); break;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnTextInput(const SDL_TextInputEvent& event)
|
||||
{
|
||||
WindowHost->OnWindowKeyChar(event.text);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnKeyUp(const SDL_KeyboardEvent& event)
|
||||
{
|
||||
WindowHost->OnWindowKeyUp(ScancodeToInputKey(event.keysym.scancode));
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnKeyDown(const SDL_KeyboardEvent& event)
|
||||
{
|
||||
WindowHost->OnWindowKeyDown(ScancodeToInputKey(event.keysym.scancode));
|
||||
}
|
||||
|
||||
EInputKey SDL2DisplayWindow::GetMouseButtonKey(const SDL_MouseButtonEvent& event)
|
||||
{
|
||||
switch (event.button)
|
||||
{
|
||||
case SDL_BUTTON_LEFT: return IK_LeftMouse;
|
||||
case SDL_BUTTON_MIDDLE: return IK_MiddleMouse;
|
||||
case SDL_BUTTON_RIGHT: return IK_RightMouse;
|
||||
// case SDL_BUTTON_X1: return IK_XButton1;
|
||||
// case SDL_BUTTON_X2: return IK_XButton2;
|
||||
default: return IK_None;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnMouseButtonUp(const SDL_MouseButtonEvent& event)
|
||||
{
|
||||
EInputKey key = GetMouseButtonKey(event);
|
||||
if (key != IK_None)
|
||||
{
|
||||
WindowHost->OnWindowMouseUp(GetMousePos(event), key);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnMouseButtonDown(const SDL_MouseButtonEvent& event)
|
||||
{
|
||||
EInputKey key = GetMouseButtonKey(event);
|
||||
if (key != IK_None)
|
||||
{
|
||||
WindowHost->OnWindowMouseDown(GetMousePos(event), key);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnMouseWheel(const SDL_MouseWheelEvent& event)
|
||||
{
|
||||
EInputKey key = (event.y > 0) ? IK_MouseWheelUp : (event.y < 0) ? IK_MouseWheelDown : IK_None;
|
||||
if (key != IK_None)
|
||||
{
|
||||
WindowHost->OnWindowMouseWheel(GetMousePos(event), key);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnMouseMotion(const SDL_MouseMotionEvent& event)
|
||||
{
|
||||
WindowHost->OnWindowMouseMove(GetMousePos(event));
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnPaintEvent()
|
||||
{
|
||||
WindowHost->OnWindowPaint();
|
||||
}
|
||||
|
||||
EInputKey SDL2DisplayWindow::ScancodeToInputKey(SDL_Scancode keycode)
|
||||
{
|
||||
switch (keycode)
|
||||
{
|
||||
case SDL_SCANCODE_BACKSPACE: return IK_Backspace;
|
||||
case SDL_SCANCODE_TAB: return IK_Tab;
|
||||
case SDL_SCANCODE_CLEAR: return IK_OEMClear;
|
||||
case SDL_SCANCODE_RETURN: return IK_Enter;
|
||||
case SDL_SCANCODE_MENU: return IK_Alt;
|
||||
case SDL_SCANCODE_PAUSE: return IK_Pause;
|
||||
case SDL_SCANCODE_ESCAPE: return IK_Escape;
|
||||
case SDL_SCANCODE_SPACE: return IK_Space;
|
||||
case SDL_SCANCODE_END: return IK_End;
|
||||
case SDL_SCANCODE_HOME: return IK_Home;
|
||||
case SDL_SCANCODE_LEFT: return IK_Left;
|
||||
case SDL_SCANCODE_UP: return IK_Up;
|
||||
case SDL_SCANCODE_RIGHT: return IK_Right;
|
||||
case SDL_SCANCODE_DOWN: return IK_Down;
|
||||
case SDL_SCANCODE_SELECT: return IK_Select;
|
||||
case SDL_SCANCODE_PRINTSCREEN: return IK_Print;
|
||||
case SDL_SCANCODE_EXECUTE: return IK_Execute;
|
||||
case SDL_SCANCODE_INSERT: return IK_Insert;
|
||||
case SDL_SCANCODE_DELETE: return IK_Delete;
|
||||
case SDL_SCANCODE_HELP: return IK_Help;
|
||||
case SDL_SCANCODE_0: return IK_0;
|
||||
case SDL_SCANCODE_1: return IK_1;
|
||||
case SDL_SCANCODE_2: return IK_2;
|
||||
case SDL_SCANCODE_3: return IK_3;
|
||||
case SDL_SCANCODE_4: return IK_4;
|
||||
case SDL_SCANCODE_5: return IK_5;
|
||||
case SDL_SCANCODE_6: return IK_6;
|
||||
case SDL_SCANCODE_7: return IK_7;
|
||||
case SDL_SCANCODE_8: return IK_8;
|
||||
case SDL_SCANCODE_9: return IK_9;
|
||||
case SDL_SCANCODE_A: return IK_A;
|
||||
case SDL_SCANCODE_B: return IK_B;
|
||||
case SDL_SCANCODE_C: return IK_C;
|
||||
case SDL_SCANCODE_D: return IK_D;
|
||||
case SDL_SCANCODE_E: return IK_E;
|
||||
case SDL_SCANCODE_F: return IK_F;
|
||||
case SDL_SCANCODE_G: return IK_G;
|
||||
case SDL_SCANCODE_H: return IK_H;
|
||||
case SDL_SCANCODE_I: return IK_I;
|
||||
case SDL_SCANCODE_J: return IK_J;
|
||||
case SDL_SCANCODE_K: return IK_K;
|
||||
case SDL_SCANCODE_L: return IK_L;
|
||||
case SDL_SCANCODE_M: return IK_M;
|
||||
case SDL_SCANCODE_N: return IK_N;
|
||||
case SDL_SCANCODE_O: return IK_O;
|
||||
case SDL_SCANCODE_P: return IK_P;
|
||||
case SDL_SCANCODE_Q: return IK_Q;
|
||||
case SDL_SCANCODE_R: return IK_R;
|
||||
case SDL_SCANCODE_S: return IK_S;
|
||||
case SDL_SCANCODE_T: return IK_T;
|
||||
case SDL_SCANCODE_U: return IK_U;
|
||||
case SDL_SCANCODE_V: return IK_V;
|
||||
case SDL_SCANCODE_W: return IK_W;
|
||||
case SDL_SCANCODE_X: return IK_X;
|
||||
case SDL_SCANCODE_Y: return IK_Y;
|
||||
case SDL_SCANCODE_Z: return IK_Z;
|
||||
case SDL_SCANCODE_KP_0: return IK_NumPad0;
|
||||
case SDL_SCANCODE_KP_1: return IK_NumPad1;
|
||||
case SDL_SCANCODE_KP_2: return IK_NumPad2;
|
||||
case SDL_SCANCODE_KP_3: return IK_NumPad3;
|
||||
case SDL_SCANCODE_KP_4: return IK_NumPad4;
|
||||
case SDL_SCANCODE_KP_5: return IK_NumPad5;
|
||||
case SDL_SCANCODE_KP_6: return IK_NumPad6;
|
||||
case SDL_SCANCODE_KP_7: return IK_NumPad7;
|
||||
case SDL_SCANCODE_KP_8: return IK_NumPad8;
|
||||
case SDL_SCANCODE_KP_9: return IK_NumPad9;
|
||||
// case SDL_SCANCODE_KP_ENTER: return IK_NumPadEnter;
|
||||
// case SDL_SCANCODE_KP_MULTIPLY: return IK_Multiply;
|
||||
// case SDL_SCANCODE_KP_PLUS: return IK_Add;
|
||||
case SDL_SCANCODE_SEPARATOR: return IK_Separator;
|
||||
// case SDL_SCANCODE_KP_MINUS: return IK_Subtract;
|
||||
case SDL_SCANCODE_KP_PERIOD: return IK_NumPadPeriod;
|
||||
// case SDL_SCANCODE_KP_DIVIDE: return IK_Divide;
|
||||
case SDL_SCANCODE_F1: return IK_F1;
|
||||
case SDL_SCANCODE_F2: return IK_F2;
|
||||
case SDL_SCANCODE_F3: return IK_F3;
|
||||
case SDL_SCANCODE_F4: return IK_F4;
|
||||
case SDL_SCANCODE_F5: return IK_F5;
|
||||
case SDL_SCANCODE_F6: return IK_F6;
|
||||
case SDL_SCANCODE_F7: return IK_F7;
|
||||
case SDL_SCANCODE_F8: return IK_F8;
|
||||
case SDL_SCANCODE_F9: return IK_F9;
|
||||
case SDL_SCANCODE_F10: return IK_F10;
|
||||
case SDL_SCANCODE_F11: return IK_F11;
|
||||
case SDL_SCANCODE_F12: return IK_F12;
|
||||
case SDL_SCANCODE_F13: return IK_F13;
|
||||
case SDL_SCANCODE_F14: return IK_F14;
|
||||
case SDL_SCANCODE_F15: return IK_F15;
|
||||
case SDL_SCANCODE_F16: return IK_F16;
|
||||
case SDL_SCANCODE_F17: return IK_F17;
|
||||
case SDL_SCANCODE_F18: return IK_F18;
|
||||
case SDL_SCANCODE_F19: return IK_F19;
|
||||
case SDL_SCANCODE_F20: return IK_F20;
|
||||
case SDL_SCANCODE_F21: return IK_F21;
|
||||
case SDL_SCANCODE_F22: return IK_F22;
|
||||
case SDL_SCANCODE_F23: return IK_F23;
|
||||
case SDL_SCANCODE_F24: return IK_F24;
|
||||
case SDL_SCANCODE_NUMLOCKCLEAR: return IK_NumLock;
|
||||
case SDL_SCANCODE_SCROLLLOCK: return IK_ScrollLock;
|
||||
case SDL_SCANCODE_LSHIFT: return IK_LShift;
|
||||
case SDL_SCANCODE_RSHIFT: return IK_RShift;
|
||||
case SDL_SCANCODE_LCTRL: return IK_LControl;
|
||||
case SDL_SCANCODE_RCTRL: return IK_RControl;
|
||||
case SDL_SCANCODE_GRAVE: return IK_Tilde;
|
||||
default: return IK_None;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Scancode SDL2DisplayWindow::InputKeyToScancode(EInputKey inputkey)
|
||||
{
|
||||
switch (inputkey)
|
||||
{
|
||||
case IK_Backspace: return SDL_SCANCODE_BACKSPACE;
|
||||
case IK_Tab: return SDL_SCANCODE_TAB;
|
||||
case IK_OEMClear: return SDL_SCANCODE_CLEAR;
|
||||
case IK_Enter: return SDL_SCANCODE_RETURN;
|
||||
case IK_Alt: return SDL_SCANCODE_MENU;
|
||||
case IK_Pause: return SDL_SCANCODE_PAUSE;
|
||||
case IK_Escape: return SDL_SCANCODE_ESCAPE;
|
||||
case IK_Space: return SDL_SCANCODE_SPACE;
|
||||
case IK_End: return SDL_SCANCODE_END;
|
||||
case IK_Home: return SDL_SCANCODE_HOME;
|
||||
case IK_Left: return SDL_SCANCODE_LEFT;
|
||||
case IK_Up: return SDL_SCANCODE_UP;
|
||||
case IK_Right: return SDL_SCANCODE_RIGHT;
|
||||
case IK_Down: return SDL_SCANCODE_DOWN;
|
||||
case IK_Select: return SDL_SCANCODE_SELECT;
|
||||
case IK_Print: return SDL_SCANCODE_PRINTSCREEN;
|
||||
case IK_Execute: return SDL_SCANCODE_EXECUTE;
|
||||
case IK_Insert: return SDL_SCANCODE_INSERT;
|
||||
case IK_Delete: return SDL_SCANCODE_DELETE;
|
||||
case IK_Help: return SDL_SCANCODE_HELP;
|
||||
case IK_0: return SDL_SCANCODE_0;
|
||||
case IK_1: return SDL_SCANCODE_1;
|
||||
case IK_2: return SDL_SCANCODE_2;
|
||||
case IK_3: return SDL_SCANCODE_3;
|
||||
case IK_4: return SDL_SCANCODE_4;
|
||||
case IK_5: return SDL_SCANCODE_5;
|
||||
case IK_6: return SDL_SCANCODE_6;
|
||||
case IK_7: return SDL_SCANCODE_7;
|
||||
case IK_8: return SDL_SCANCODE_8;
|
||||
case IK_9: return SDL_SCANCODE_9;
|
||||
case IK_A: return SDL_SCANCODE_A;
|
||||
case IK_B: return SDL_SCANCODE_B;
|
||||
case IK_C: return SDL_SCANCODE_C;
|
||||
case IK_D: return SDL_SCANCODE_D;
|
||||
case IK_E: return SDL_SCANCODE_E;
|
||||
case IK_F: return SDL_SCANCODE_F;
|
||||
case IK_G: return SDL_SCANCODE_G;
|
||||
case IK_H: return SDL_SCANCODE_H;
|
||||
case IK_I: return SDL_SCANCODE_I;
|
||||
case IK_J: return SDL_SCANCODE_J;
|
||||
case IK_K: return SDL_SCANCODE_K;
|
||||
case IK_L: return SDL_SCANCODE_L;
|
||||
case IK_M: return SDL_SCANCODE_M;
|
||||
case IK_N: return SDL_SCANCODE_N;
|
||||
case IK_O: return SDL_SCANCODE_O;
|
||||
case IK_P: return SDL_SCANCODE_P;
|
||||
case IK_Q: return SDL_SCANCODE_Q;
|
||||
case IK_R: return SDL_SCANCODE_R;
|
||||
case IK_S: return SDL_SCANCODE_S;
|
||||
case IK_T: return SDL_SCANCODE_T;
|
||||
case IK_U: return SDL_SCANCODE_U;
|
||||
case IK_V: return SDL_SCANCODE_V;
|
||||
case IK_W: return SDL_SCANCODE_W;
|
||||
case IK_X: return SDL_SCANCODE_X;
|
||||
case IK_Y: return SDL_SCANCODE_Y;
|
||||
case IK_Z: return SDL_SCANCODE_Z;
|
||||
case IK_NumPad0: return SDL_SCANCODE_KP_0;
|
||||
case IK_NumPad1: return SDL_SCANCODE_KP_1;
|
||||
case IK_NumPad2: return SDL_SCANCODE_KP_2;
|
||||
case IK_NumPad3: return SDL_SCANCODE_KP_3;
|
||||
case IK_NumPad4: return SDL_SCANCODE_KP_4;
|
||||
case IK_NumPad5: return SDL_SCANCODE_KP_5;
|
||||
case IK_NumPad6: return SDL_SCANCODE_KP_6;
|
||||
case IK_NumPad7: return SDL_SCANCODE_KP_7;
|
||||
case IK_NumPad8: return SDL_SCANCODE_KP_8;
|
||||
case IK_NumPad9: return SDL_SCANCODE_KP_9;
|
||||
// case IK_NumPadEnter: return SDL_SCANCODE_KP_ENTER;
|
||||
// case IK_Multiply return SDL_SCANCODE_KP_MULTIPLY:;
|
||||
// case IK_Add: return SDL_SCANCODE_KP_PLUS;
|
||||
case IK_Separator: return SDL_SCANCODE_SEPARATOR;
|
||||
// case IK_Subtract: return SDL_SCANCODE_KP_MINUS;
|
||||
case IK_NumPadPeriod: return SDL_SCANCODE_KP_PERIOD;
|
||||
// case IK_Divide: return SDL_SCANCODE_KP_DIVIDE;
|
||||
case IK_F1: return SDL_SCANCODE_F1;
|
||||
case IK_F2: return SDL_SCANCODE_F2;
|
||||
case IK_F3: return SDL_SCANCODE_F3;
|
||||
case IK_F4: return SDL_SCANCODE_F4;
|
||||
case IK_F5: return SDL_SCANCODE_F5;
|
||||
case IK_F6: return SDL_SCANCODE_F6;
|
||||
case IK_F7: return SDL_SCANCODE_F7;
|
||||
case IK_F8: return SDL_SCANCODE_F8;
|
||||
case IK_F9: return SDL_SCANCODE_F9;
|
||||
case IK_F10: return SDL_SCANCODE_F10;
|
||||
case IK_F11: return SDL_SCANCODE_F11;
|
||||
case IK_F12: return SDL_SCANCODE_F12;
|
||||
case IK_F13: return SDL_SCANCODE_F13;
|
||||
case IK_F14: return SDL_SCANCODE_F14;
|
||||
case IK_F15: return SDL_SCANCODE_F15;
|
||||
case IK_F16: return SDL_SCANCODE_F16;
|
||||
case IK_F17: return SDL_SCANCODE_F17;
|
||||
case IK_F18: return SDL_SCANCODE_F18;
|
||||
case IK_F19: return SDL_SCANCODE_F19;
|
||||
case IK_F20: return SDL_SCANCODE_F20;
|
||||
case IK_F21: return SDL_SCANCODE_F21;
|
||||
case IK_F22: return SDL_SCANCODE_F22;
|
||||
case IK_F23: return SDL_SCANCODE_F23;
|
||||
case IK_F24: return SDL_SCANCODE_F24;
|
||||
case IK_NumLock: return SDL_SCANCODE_NUMLOCKCLEAR;
|
||||
case IK_ScrollLock: return SDL_SCANCODE_SCROLLLOCK;
|
||||
case IK_LShift: return SDL_SCANCODE_LSHIFT;
|
||||
case IK_RShift: return SDL_SCANCODE_RSHIFT;
|
||||
case IK_LControl: return SDL_SCANCODE_LCTRL;
|
||||
case IK_RControl: return SDL_SCANCODE_RCTRL;
|
||||
case IK_Tilde: return SDL_SCANCODE_GRAVE;
|
||||
default: return (SDL_Scancode)0;
|
||||
}
|
||||
}
|
||||
64
libraries/ZWidget/src/window/stub/stub_open_file_dialog.cpp
Normal file
64
libraries/ZWidget/src/window/stub/stub_open_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
#include "stub_open_file_dialog.h"
|
||||
|
||||
StubOpenFileDialog::StubOpenFileDialog(DisplayWindow* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
bool StubOpenFileDialog::Show()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string StubOpenFileDialog::Filename() const
|
||||
{
|
||||
return !filenames.empty() ? filenames.front() : std::string();
|
||||
}
|
||||
|
||||
std::vector<std::string> StubOpenFileDialog::Filenames() const
|
||||
{
|
||||
return filenames;
|
||||
}
|
||||
|
||||
void StubOpenFileDialog::SetMultiSelect(bool new_multi_select)
|
||||
{
|
||||
multi_select = new_multi_select;
|
||||
}
|
||||
|
||||
void StubOpenFileDialog::SetFilename(const std::string& filename)
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void StubOpenFileDialog::AddFilter(const std::string& filter_description, const std::string& filter_extension)
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void StubOpenFileDialog::ClearFilters()
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void StubOpenFileDialog::SetFilterIndex(int filter_index)
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void StubOpenFileDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void StubOpenFileDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void StubOpenFileDialog::SetDefaultExtension(const std::string& extension)
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
41
libraries/ZWidget/src/window/stub/stub_open_file_dialog.h
Normal file
41
libraries/ZWidget/src/window/stub/stub_open_file_dialog.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/open_file_dialog.h"
|
||||
|
||||
class DisplayWindow;
|
||||
|
||||
class StubOpenFileDialog : public OpenFileDialog
|
||||
{
|
||||
public:
|
||||
StubOpenFileDialog(DisplayWindow* owner);
|
||||
|
||||
bool Show() override;
|
||||
std::string Filename() const override;
|
||||
std::vector<std::string> Filenames() const override;
|
||||
void SetMultiSelect(bool new_multi_select) override;
|
||||
void SetFilename(const std::string& filename) override;
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override;
|
||||
void ClearFilters() override;
|
||||
void SetFilterIndex(int filter_index) override;
|
||||
void SetInitialDirectory(const std::string& path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
void SetDefaultExtension(const std::string& extension) override;
|
||||
|
||||
private:
|
||||
DisplayWindow* owner = nullptr;
|
||||
|
||||
std::string initial_directory;
|
||||
std::string initial_filename;
|
||||
std::string title;
|
||||
std::vector<std::string> filenames;
|
||||
bool multi_select = false;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string description;
|
||||
std::string extension;
|
||||
};
|
||||
std::vector<Filter> filters;
|
||||
int filterindex = 0;
|
||||
std::string defaultext;
|
||||
};
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
#include "stub_open_folder_dialog.h"
|
||||
|
||||
StubOpenFolderDialog::StubOpenFolderDialog(DisplayWindow* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
bool StubOpenFolderDialog::Show()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string StubOpenFolderDialog::SelectedPath() const
|
||||
{
|
||||
return selected_path;
|
||||
}
|
||||
|
||||
void StubOpenFolderDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void StubOpenFolderDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
23
libraries/ZWidget/src/window/stub/stub_open_folder_dialog.h
Normal file
23
libraries/ZWidget/src/window/stub/stub_open_folder_dialog.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/open_folder_dialog.h"
|
||||
|
||||
class DisplayWindow;
|
||||
|
||||
class StubOpenFolderDialog : public OpenFolderDialog
|
||||
{
|
||||
public:
|
||||
StubOpenFolderDialog(DisplayWindow* owner);
|
||||
|
||||
bool Show() override;
|
||||
std::string SelectedPath() const override;
|
||||
void SetInitialDirectory(const std::string& path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
|
||||
private:
|
||||
DisplayWindow* owner = nullptr;
|
||||
|
||||
std::string selected_path;
|
||||
std::string initial_directory;
|
||||
std::string title;
|
||||
};
|
||||
54
libraries/ZWidget/src/window/stub/stub_save_file_dialog.cpp
Normal file
54
libraries/ZWidget/src/window/stub/stub_save_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
#include "stub_save_file_dialog.h"
|
||||
|
||||
StubSaveFileDialog::StubSaveFileDialog(DisplayWindow* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
bool StubSaveFileDialog::Show()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string StubSaveFileDialog::Filename() const
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
void StubSaveFileDialog::SetFilename(const std::string& filename)
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void StubSaveFileDialog::AddFilter(const std::string& filter_description, const std::string& filter_extension)
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void StubSaveFileDialog::ClearFilters()
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void StubSaveFileDialog::SetFilterIndex(int filter_index)
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void StubSaveFileDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void StubSaveFileDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void StubSaveFileDialog::SetDefaultExtension(const std::string& extension)
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
39
libraries/ZWidget/src/window/stub/stub_save_file_dialog.h
Normal file
39
libraries/ZWidget/src/window/stub/stub_save_file_dialog.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/save_file_dialog.h"
|
||||
|
||||
class DisplayWindow;
|
||||
|
||||
class StubSaveFileDialog : public SaveFileDialog
|
||||
{
|
||||
public:
|
||||
StubSaveFileDialog(DisplayWindow* owner);
|
||||
|
||||
bool Show() override;
|
||||
std::string Filename() const override;
|
||||
void SetFilename(const std::string& filename) override;
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override;
|
||||
void ClearFilters() override;
|
||||
void SetFilterIndex(int filter_index) override;
|
||||
void SetInitialDirectory(const std::string& path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
void SetDefaultExtension(const std::string& extension) override;
|
||||
|
||||
private:
|
||||
DisplayWindow* owner = nullptr;
|
||||
|
||||
std::string filename;
|
||||
std::string initial_directory;
|
||||
std::string initial_filename;
|
||||
std::string title;
|
||||
std::vector<std::string> filenames;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string description;
|
||||
std::string extension;
|
||||
};
|
||||
std::vector<Filter> filters;
|
||||
int filterindex = 0;
|
||||
std::string defaultext;
|
||||
};
|
||||
1150
libraries/ZWidget/src/window/wayland/wayland_display_backend.cpp
Normal file
1150
libraries/ZWidget/src/window/wayland/wayland_display_backend.cpp
Normal file
File diff suppressed because it is too large
Load diff
168
libraries/ZWidget/src/window/wayland/wayland_display_backend.h
Normal file
168
libraries/ZWidget/src/window/wayland/wayland_display_backend.h
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
#pragma once
|
||||
|
||||
#include "window/window.h"
|
||||
#include "window/ztimer/ztimer.h"
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-client.hpp>
|
||||
#include <wayland-client-protocol-extra.hpp>
|
||||
#include <wayland-client-protocol-unstable.hpp>
|
||||
#include <wayland-cursor.hpp>
|
||||
#include "wl_fractional_scaling_protocol.hpp"
|
||||
#include <linux/input.h>
|
||||
#include <poll.h>
|
||||
#include <map>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
static short poll_single(int fd, short events, int timeout) {
|
||||
pollfd pfd { .fd = fd, .events = events, .revents = 0 };
|
||||
if (0 > poll(&pfd, 1, timeout)) {
|
||||
throw std::runtime_error("poll() failed");
|
||||
}
|
||||
|
||||
return pfd.revents;
|
||||
}
|
||||
|
||||
enum pointer_event_mask {
|
||||
POINTER_EVENT_ENTER = 1 << 0,
|
||||
POINTER_EVENT_LEAVE = 1 << 1,
|
||||
POINTER_EVENT_MOTION = 1 << 2,
|
||||
POINTER_EVENT_BUTTON = 1 << 3,
|
||||
POINTER_EVENT_AXIS = 1 << 4,
|
||||
POINTER_EVENT_AXIS_SOURCE = 1 << 5,
|
||||
POINTER_EVENT_AXIS_STOP = 1 << 6,
|
||||
POINTER_EVENT_AXIS_DISCRETE = 1 << 7,
|
||||
POINTER_EVENT_AXIS_120 = 1 << 8,
|
||||
POINTER_EVENT_RELATIVE_MOTION = 1 << 9,
|
||||
};
|
||||
|
||||
struct WaylandPointerEvent
|
||||
{
|
||||
uint32_t event_mask;
|
||||
double surfaceX, surfaceY;
|
||||
double dx, dy;
|
||||
uint32_t button;
|
||||
wayland::pointer_button_state state;
|
||||
uint32_t time;
|
||||
uint32_t serial;
|
||||
struct {
|
||||
bool valid;
|
||||
double value;
|
||||
int32_t discrete;
|
||||
int32_t value120;
|
||||
} axes[2];
|
||||
wayland::pointer_axis_source axis_source;
|
||||
};
|
||||
|
||||
class WaylandDisplayWindow;
|
||||
|
||||
class WaylandDisplayBackend : public DisplayBackend
|
||||
{
|
||||
public:
|
||||
WaylandDisplayBackend();
|
||||
~WaylandDisplayBackend();
|
||||
|
||||
std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI) override;
|
||||
void ProcessEvents() override;
|
||||
void RunLoop() override;
|
||||
void ExitLoop() override;
|
||||
|
||||
void* StartTimer(int timeoutMilliseconds, std::function<void()> onTimer) override;
|
||||
void StopTimer(void* timerID) override;
|
||||
|
||||
Size GetScreenSize() override;
|
||||
|
||||
bool IsWayland() override { return true; }
|
||||
|
||||
void OnWindowCreated(WaylandDisplayWindow* window);
|
||||
void OnWindowDestroyed(WaylandDisplayWindow* window);
|
||||
|
||||
void SetCursor(StandardCursor cursor);
|
||||
void ShowCursor(bool enable);
|
||||
bool GetKeyState(InputKey key);
|
||||
|
||||
#ifdef USE_DBUS
|
||||
std::unique_ptr<OpenFileDialog> CreateOpenFileDialog(DisplayWindow* owner) override;
|
||||
std::unique_ptr<SaveFileDialog> CreateSaveFileDialog(DisplayWindow* owner) override;
|
||||
std::unique_ptr<OpenFolderDialog> CreateOpenFolderDialog(DisplayWindow* owner) override;
|
||||
#endif
|
||||
|
||||
bool exitRunLoop = false;
|
||||
Size s_ScreenSize = Size(0, 0);
|
||||
wayland::display_t s_waylandDisplay = wayland::display_t();
|
||||
wayland::registry_t s_waylandRegistry;
|
||||
std::vector<WaylandDisplayWindow*> s_Windows;
|
||||
WaylandDisplayWindow* m_FocusWindow = nullptr;
|
||||
WaylandDisplayWindow* m_MouseFocusWindow = nullptr; // Mouse focus should be tracked separately.
|
||||
|
||||
wayland::compositor_t m_waylandCompositor;
|
||||
wayland::shm_t m_waylandSHM;
|
||||
wayland::seat_t m_waylandSeat;
|
||||
wayland::output_t m_waylandOutput;
|
||||
wayland::data_device_manager_t m_DataDeviceManager;
|
||||
wayland::xdg_wm_base_t m_XDGWMBase;
|
||||
wayland::zwp_pointer_constraints_v1_t m_PointerConstraints;
|
||||
wayland::xdg_activation_v1_t m_XDGActivation;
|
||||
wayland::zxdg_decoration_manager_v1_t m_XDGDecorationManager;
|
||||
wayland::fractional_scale_manager_v1_t m_FractionalScaleManager;
|
||||
wayland::zxdg_output_manager_v1_t m_XDGOutputManager;
|
||||
wayland::zxdg_output_v1_t m_XDGOutput;
|
||||
wayland::zxdg_exporter_v2_t m_XDGExporter;
|
||||
|
||||
wayland::keyboard_t m_waylandKeyboard;
|
||||
wayland::pointer_t m_waylandPointer;
|
||||
|
||||
wayland::zwp_relative_pointer_manager_v1_t m_RelativePointerManager;
|
||||
wayland::zwp_relative_pointer_v1_t m_RelativePointer;
|
||||
|
||||
wayland::cursor_image_t m_cursorImage;
|
||||
wayland::surface_t m_cursorSurface;
|
||||
wayland::buffer_t m_cursorBuffer;
|
||||
|
||||
std::map<InputKey, bool> inputKeyStates; // True when the key is pressed, false when isn't
|
||||
|
||||
bool IsMouseLocked() { return hasMouseLock; }
|
||||
void SetMouseLocked(bool val) { hasMouseLock = val; }
|
||||
|
||||
private:
|
||||
void CheckNeedsUpdate();
|
||||
void UpdateTimers();
|
||||
void ConnectDeviceEvents();
|
||||
void OnKeyboardKeyEvent(xkb_keysym_t xkbKeySym, wayland::keyboard_key_state state);
|
||||
void OnKeyboardCharEvent(const char* ch, wayland::keyboard_key_state state);
|
||||
void OnKeyboardDelayEnd();
|
||||
void OnKeyboardRepeat();
|
||||
void OnMouseEnterEvent(uint32_t serial);
|
||||
void OnMouseLeaveEvent();
|
||||
void OnMousePressEvent(InputKey button);
|
||||
void OnMouseReleaseEvent(InputKey button);
|
||||
void OnMouseMoveEvent(Point surfacePos);
|
||||
void OnMouseMoveRawEvent(int surfaceX, int surfaceY);
|
||||
void OnMouseWheelEvent(InputKey button);
|
||||
|
||||
InputKey XKBKeySymToInputKey(xkb_keysym_t keySym);
|
||||
InputKey LinuxInputEventCodeToInputKey(uint32_t inputCode);
|
||||
|
||||
std::string GetWaylandCursorName(StandardCursor cursor);
|
||||
|
||||
bool hasKeyboard = false;
|
||||
bool hasPointer = false;
|
||||
bool hasMouseLock = false;
|
||||
|
||||
ZTimer::TimePoint m_previousTime;
|
||||
ZTimer::TimePoint m_currentTime;
|
||||
|
||||
ZTimer m_keyboardDelayTimer;
|
||||
ZTimer m_keyboardRepeatTimer;
|
||||
|
||||
InputKey previousKey = {};
|
||||
std::string previousChars;
|
||||
|
||||
uint32_t m_KeyboardSerial = 0;
|
||||
|
||||
xkb_context* m_KeymapContext = nullptr;
|
||||
xkb_keymap* m_Keymap = nullptr;
|
||||
xkb_state* m_KeyboardState = nullptr;
|
||||
|
||||
WaylandPointerEvent currentPointerEvent = {0};
|
||||
};
|
||||
472
libraries/ZWidget/src/window/wayland/wayland_display_window.cpp
Normal file
472
libraries/ZWidget/src/window/wayland/wayland_display_window.cpp
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
#include "wayland_display_window.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
|
||||
WaylandDisplayWindow::WaylandDisplayWindow(WaylandDisplayBackend* backend, DisplayWindowHost* windowHost, bool popupWindow, WaylandDisplayWindow* owner, RenderAPI renderAPI)
|
||||
: backend(backend), m_owner(owner), windowHost(windowHost), m_PopupWindow(popupWindow), m_renderAPI(renderAPI)
|
||||
{
|
||||
m_AppSurface = backend->m_waylandCompositor.create_surface();
|
||||
|
||||
m_NativeHandle.display = backend->s_waylandDisplay;
|
||||
m_NativeHandle.surface = m_AppSurface;
|
||||
|
||||
if (backend->m_FractionalScaleManager)
|
||||
{
|
||||
m_FractionalScale = backend->m_FractionalScaleManager.get_fractional_scale(m_AppSurface);
|
||||
|
||||
m_FractionalScale.on_preferred_scale() = [&](uint32_t scale_numerator) {
|
||||
// parameter is the numerator of a fraction with the denominator of 120
|
||||
m_ScaleFactor = scale_numerator / 120.0;
|
||||
|
||||
m_NeedsUpdate = true;
|
||||
windowHost->OnWindowDpiScaleChanged();
|
||||
};
|
||||
}
|
||||
|
||||
m_XDGSurface = backend->m_XDGWMBase.get_xdg_surface(m_AppSurface);
|
||||
m_XDGSurface.on_configure() = [&] (uint32_t serial) {
|
||||
m_XDGSurface.ack_configure(serial);
|
||||
};
|
||||
|
||||
if (popupWindow)
|
||||
{
|
||||
InitializePopup();
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeToplevel();
|
||||
}
|
||||
|
||||
backend->OnWindowCreated(this);
|
||||
|
||||
this->DrawSurface();
|
||||
}
|
||||
|
||||
WaylandDisplayWindow::~WaylandDisplayWindow()
|
||||
{
|
||||
backend->OnWindowDestroyed(this);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::InitializeToplevel()
|
||||
{
|
||||
m_XDGToplevel = m_XDGSurface.get_toplevel();
|
||||
m_XDGToplevel.set_title("ZWidget Window");
|
||||
|
||||
if (m_owner)
|
||||
m_XDGToplevel.set_parent(m_owner->m_XDGToplevel);
|
||||
|
||||
if (backend->m_XDGDecorationManager)
|
||||
{
|
||||
// Force server side decorations if possible
|
||||
m_XDGToplevelDecoration = backend->m_XDGDecorationManager.get_toplevel_decoration(m_XDGToplevel);
|
||||
m_XDGToplevelDecoration.set_mode(wayland::zxdg_toplevel_decoration_v1_mode::server_side);
|
||||
}
|
||||
|
||||
m_AppSurface.commit();
|
||||
|
||||
backend->s_waylandDisplay.roundtrip();
|
||||
|
||||
// These have to be added after the roundtrip
|
||||
m_XDGToplevel.on_configure() = [&] (int32_t width, int32_t height, wayland::array_t states) {
|
||||
OnXDGToplevelConfigureEvent(width, height);
|
||||
};
|
||||
|
||||
m_XDGToplevel.on_close() = [&] () {
|
||||
OnExitEvent();
|
||||
};
|
||||
|
||||
m_XDGToplevel.on_configure_bounds() = [this] (int32_t width, int32_t height)
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
m_XDGExported = backend->m_XDGExporter.export_toplevel(m_AppSurface);
|
||||
|
||||
m_XDGExported.on_handle() = [&] (std::string handleStr) {
|
||||
OnExportHandleEvent(handleStr);
|
||||
};
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::InitializePopup()
|
||||
{
|
||||
if (!m_owner)
|
||||
throw std::runtime_error("Popup window must have an owner!");
|
||||
|
||||
wayland::xdg_positioner_t popupPositioner = backend->m_XDGWMBase.create_positioner();
|
||||
|
||||
popupPositioner.set_anchor(wayland::xdg_positioner_anchor::bottom);
|
||||
popupPositioner.set_anchor_rect(0, 0, 1, 30);
|
||||
popupPositioner.set_size(1, 1);
|
||||
|
||||
m_XDGPopup = m_XDGSurface.get_popup(m_owner->m_XDGSurface, popupPositioner);
|
||||
|
||||
m_XDGPopup.on_configure() = [&] (int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||
SetClientFrame(Rect::xywh(x, y, width, height));
|
||||
};
|
||||
|
||||
//m_XDGPopup.on_repositioned()
|
||||
|
||||
m_XDGPopup.on_popup_done() = [&] () {
|
||||
OnExitEvent();
|
||||
};
|
||||
|
||||
m_AppSurface.commit();
|
||||
|
||||
backend->s_waylandDisplay.roundtrip();
|
||||
}
|
||||
|
||||
|
||||
void WaylandDisplayWindow::SetWindowTitle(const std::string& text)
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_title(text);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetWindowFrame(const Rect& box)
|
||||
{
|
||||
// Resizing will be shown on the next commit
|
||||
CreateBuffers(box.width, box.height);
|
||||
windowHost->OnWindowGeometryChanged();
|
||||
m_NeedsUpdate = true;
|
||||
m_AppSurface.commit();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetClientFrame(const Rect& box)
|
||||
{
|
||||
SetWindowFrame(box);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::Show()
|
||||
{
|
||||
m_AppSurface.attach(m_AppSurfaceBuffer, 0, 0);
|
||||
m_AppSurface.damage(0, 0, m_WindowSize.width, m_WindowSize.height);
|
||||
m_AppSurface.commit();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowFullscreen()
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
{
|
||||
m_XDGToplevel.set_fullscreen(backend->m_waylandOutput);
|
||||
isFullscreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowMaximized()
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_maximized();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowMinimized()
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.set_minimized();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowNormal()
|
||||
{
|
||||
if (m_XDGToplevel)
|
||||
m_XDGToplevel.unset_fullscreen();
|
||||
}
|
||||
|
||||
bool WaylandDisplayWindow::IsWindowFullscreen()
|
||||
{
|
||||
return isFullscreen;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::Hide()
|
||||
{
|
||||
// Apparently this is how hiding a window works
|
||||
// By attaching a null buffer to the surface
|
||||
// See: https://lists.freedesktop.org/archives/wayland-devel/2017-November/035963.html
|
||||
m_AppSurface.attach(nullptr, 0, 0);
|
||||
m_AppSurface.commit();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::Activate()
|
||||
{
|
||||
// To do: this needs to be in the backend instance if all windows share the activation token
|
||||
wayland::xdg_activation_token_v1_t xdgActivationToken = backend->m_XDGActivation.get_activation_token();
|
||||
|
||||
std::string tokenString;
|
||||
|
||||
xdgActivationToken.on_done() = [&tokenString] (std::string obtainedString) {
|
||||
tokenString = obtainedString;
|
||||
};
|
||||
|
||||
xdgActivationToken.set_surface(m_AppSurface);
|
||||
xdgActivationToken.commit(); // This will set our token string
|
||||
|
||||
backend->m_XDGActivation.activate(tokenString, m_AppSurface);
|
||||
backend->m_FocusWindow = this;
|
||||
backend->m_MouseFocusWindow = this;
|
||||
windowHost->OnWindowActivated();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ShowCursor(bool enable)
|
||||
{
|
||||
backend->ShowCursor(enable);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::LockCursor()
|
||||
{
|
||||
m_LockedPointer = backend->m_PointerConstraints.lock_pointer(m_AppSurface, backend->m_waylandPointer, nullptr, wayland::zwp_pointer_constraints_v1_lifetime::persistent);
|
||||
backend->SetMouseLocked(true);
|
||||
ShowCursor(false);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::UnlockCursor()
|
||||
{
|
||||
if (m_LockedPointer)
|
||||
m_LockedPointer.proxy_release();
|
||||
backend->SetMouseLocked(false);
|
||||
ShowCursor(true);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::CaptureMouse()
|
||||
{
|
||||
m_ConfinedPointer = backend->m_PointerConstraints.confine_pointer(GetWindowSurface(), backend->m_waylandPointer, nullptr, wayland::zwp_pointer_constraints_v1_lifetime::persistent);
|
||||
ShowCursor(false);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::ReleaseMouseCapture()
|
||||
{
|
||||
if (m_ConfinedPointer)
|
||||
m_ConfinedPointer.proxy_release();
|
||||
ShowCursor(true);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::Update()
|
||||
{
|
||||
m_NeedsUpdate = true;
|
||||
}
|
||||
|
||||
bool WaylandDisplayWindow::GetKeyState(InputKey key)
|
||||
{
|
||||
return backend->GetKeyState(key);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetCursor(StandardCursor cursor)
|
||||
{
|
||||
backend->SetCursor(cursor);
|
||||
}
|
||||
|
||||
Rect WaylandDisplayWindow::GetWindowFrame() const
|
||||
{
|
||||
return Rect(m_WindowGlobalPos.x, m_WindowGlobalPos.y, m_WindowSize.width, m_WindowSize.height);
|
||||
}
|
||||
|
||||
Size WaylandDisplayWindow::GetClientSize() const
|
||||
{
|
||||
return m_WindowSize / m_ScaleFactor;
|
||||
}
|
||||
|
||||
int WaylandDisplayWindow::GetPixelWidth() const
|
||||
{
|
||||
return m_WindowSize.width;
|
||||
}
|
||||
|
||||
int WaylandDisplayWindow::GetPixelHeight() const
|
||||
{
|
||||
return m_WindowSize.height;
|
||||
}
|
||||
|
||||
double WaylandDisplayWindow::GetDpiScale() const
|
||||
{
|
||||
return m_ScaleFactor;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::PresentBitmap(int width, int height, const uint32_t* pixels)
|
||||
{
|
||||
// Make new buffers if the sizes don't match
|
||||
if (width != m_WindowSize.width || height != m_WindowSize.height)
|
||||
CreateBuffers(width, height);
|
||||
|
||||
std::memcpy(shared_mem->get_mem(), (void*)pixels, width * height * 4);
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetBorderColor(uint32_t bgra8)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetCaptionColor(uint32_t bgra8)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetCaptionTextColor(uint32_t bgra8)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string WaylandDisplayWindow::GetClipboardText()
|
||||
{
|
||||
return m_ClipboardContents;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::SetClipboardText(const std::string& text)
|
||||
{
|
||||
m_ClipboardContents = text;
|
||||
}
|
||||
|
||||
Point WaylandDisplayWindow::MapFromGlobal(const Point& pos) const
|
||||
{
|
||||
return (pos - m_WindowGlobalPos) / m_ScaleFactor;
|
||||
}
|
||||
|
||||
Point WaylandDisplayWindow::MapToGlobal(const Point& pos) const
|
||||
{
|
||||
return (m_WindowGlobalPos + pos) / m_ScaleFactor;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::OnXDGToplevelConfigureEvent(int32_t width, int32_t height)
|
||||
{
|
||||
Rect rect = GetWindowFrame();
|
||||
rect.width = width / m_ScaleFactor;
|
||||
rect.height = height / m_ScaleFactor;
|
||||
SetWindowFrame(rect);
|
||||
windowHost->OnWindowGeometryChanged();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::OnExportHandleEvent(std::string exportedHandle)
|
||||
{
|
||||
m_windowID = exportedHandle;
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::OnExitEvent()
|
||||
{
|
||||
windowHost->OnWindowClose();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::DrawSurface(uint32_t serial)
|
||||
{
|
||||
m_AppSurface.attach(m_AppSurfaceBuffer, 0, 0);
|
||||
m_AppSurface.damage(0, 0, m_WindowSize.width, m_WindowSize.height);
|
||||
|
||||
if (m_renderAPI == RenderAPI::Unspecified || m_renderAPI == RenderAPI::Bitmap)
|
||||
{
|
||||
m_FrameCallback = m_AppSurface.frame();
|
||||
|
||||
m_FrameCallback.on_done() = bind_mem_fn(&WaylandDisplayWindow::DrawSurface, this);
|
||||
}
|
||||
m_AppSurface.commit();
|
||||
}
|
||||
|
||||
void WaylandDisplayWindow::CreateBuffers(int32_t width, int32_t height)
|
||||
{
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
if (shared_mem)
|
||||
shared_mem.reset();
|
||||
|
||||
int scaled_width = width * m_ScaleFactor;
|
||||
int scaled_height = height * m_ScaleFactor;
|
||||
|
||||
shared_mem = std::make_shared<SharedMemHelper>(scaled_width * scaled_height * 4);
|
||||
auto pool = backend->m_waylandSHM.create_pool(shared_mem->get_fd(), scaled_width * scaled_height * 4);
|
||||
|
||||
m_AppSurfaceBuffer = pool.create_buffer(0, scaled_width, scaled_height, scaled_width * 4, wayland::shm_format::xrgb8888);
|
||||
|
||||
m_WindowSize = Size(scaled_width, scaled_height);
|
||||
}
|
||||
|
||||
std::string WaylandDisplayWindow::GetWaylandWindowID()
|
||||
{
|
||||
return m_windowID;
|
||||
}
|
||||
|
||||
// This is to avoid needing all the Vulkan headers and the volk binding library just for this:
|
||||
#ifndef VK_VERSION_1_0
|
||||
|
||||
#define VKAPI_CALL
|
||||
#define VKAPI_PTR VKAPI_CALL
|
||||
|
||||
typedef uint32_t VkFlags;
|
||||
typedef enum VkStructureType { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType;
|
||||
typedef enum VkResult { VK_SUCCESS = 0, VK_RESULT_MAX_ENUM = 0x7FFFFFFF } VkResult;
|
||||
typedef struct VkAllocationCallbacks VkAllocationCallbacks;
|
||||
|
||||
typedef void (VKAPI_PTR* PFN_vkVoidFunction)(void);
|
||||
typedef PFN_vkVoidFunction(VKAPI_PTR* PFN_vkGetInstanceProcAddr)(VkInstance instance, const char* pName);
|
||||
|
||||
#ifndef VK_KHR_wayland_surface
|
||||
|
||||
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
|
||||
typedef struct VkWaylandSurfaceCreateInfoKHR
|
||||
{
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkWaylandSurfaceCreateFlagsKHR flags;
|
||||
struct wl_display* display;
|
||||
struct wl_surface* surface;
|
||||
} VkWaylandSurfaceCreateInfoKHR;
|
||||
|
||||
typedef VkResult(VKAPI_PTR* PFN_vkCreateWaylandSurfaceKHR)(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class ZWidgetWaylandVulkanLoader
|
||||
{
|
||||
public:
|
||||
ZWidgetWaylandVulkanLoader()
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
module = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL);
|
||||
if (!module)
|
||||
module = dlopen("libvulkan.1.dylib", RTLD_NOW | RTLD_LOCAL);
|
||||
if (!module)
|
||||
module = dlopen("libMoltenVK.dylib", RTLD_NOW | RTLD_LOCAL);
|
||||
#else
|
||||
module = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
|
||||
if (!module)
|
||||
module = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
|
||||
#endif
|
||||
|
||||
if (!module)
|
||||
throw std::runtime_error("Could not load vulkan");
|
||||
|
||||
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(module, "vkGetInstanceProcAddr");
|
||||
if (!vkGetInstanceProcAddr)
|
||||
{
|
||||
dlclose(module);
|
||||
throw std::runtime_error("vkGetInstanceProcAddr not found");
|
||||
}
|
||||
}
|
||||
|
||||
~ZWidgetWaylandVulkanLoader()
|
||||
{
|
||||
dlclose(module);
|
||||
}
|
||||
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr;
|
||||
void* module = nullptr;
|
||||
};
|
||||
|
||||
VkSurfaceKHR WaylandDisplayWindow::CreateVulkanSurface(VkInstance instance)
|
||||
{
|
||||
static ZWidgetWaylandVulkanLoader loader;
|
||||
|
||||
auto vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)loader.vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR");
|
||||
if (!vkCreateWaylandSurfaceKHR)
|
||||
throw std::runtime_error("Could not create vulkan surface");
|
||||
|
||||
VkWaylandSurfaceCreateInfoKHR createInfo = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR };
|
||||
createInfo.display = m_NativeHandle.display;
|
||||
createInfo.surface = m_NativeHandle.surface;
|
||||
|
||||
VkSurfaceKHR surface = {};
|
||||
VkResult result = vkCreateWaylandSurfaceKHR(instance, &createInfo, nullptr, &surface);
|
||||
if (result != VK_SUCCESS)
|
||||
throw std::runtime_error("Could not create vulkan surface");
|
||||
return surface;
|
||||
}
|
||||
|
||||
std::vector<std::string> WaylandDisplayWindow::GetVulkanInstanceExtensions()
|
||||
{
|
||||
return { "VK_KHR_surface", "VK_KHR_wayland_surface" };
|
||||
}
|
||||
198
libraries/ZWidget/src/window/wayland/wayland_display_window.h
Normal file
198
libraries/ZWidget/src/window/wayland/wayland_display_window.h
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
#pragma once
|
||||
|
||||
#include "wayland_display_backend.h"
|
||||
#include "window/ztimer/ztimer.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "zwidget/window/window.h"
|
||||
#include "zwidget/window/waylandnativehandle.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
template <typename R, typename T, typename... Args>
|
||||
std::function<R(Args...)> bind_mem_fn(R(T::* func)(Args...), T *t)
|
||||
{
|
||||
return [func, t] (Args... args)
|
||||
{
|
||||
return (t->*func)(args...);
|
||||
};
|
||||
}
|
||||
|
||||
class SharedMemHelper
|
||||
{
|
||||
public:
|
||||
SharedMemHelper(size_t size)
|
||||
: len(size)
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::random_device device;
|
||||
std::default_random_engine engine(device());
|
||||
std::uniform_int_distribution<unsigned int> distribution(0, std::numeric_limits<unsigned int>::max());
|
||||
ss << distribution(engine);
|
||||
name = ss.str();
|
||||
|
||||
fd = memfd_create(name.c_str(), 0);
|
||||
if(fd < 0)
|
||||
throw std::runtime_error("shm_open failed.");
|
||||
|
||||
if(ftruncate(fd, size) < 0)
|
||||
throw std::runtime_error("ftruncate failed.");
|
||||
|
||||
mem = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if(mem == MAP_FAILED) // NOLINT
|
||||
throw std::runtime_error("mmap failed with len " + std::to_string(len) + ".");
|
||||
}
|
||||
|
||||
~SharedMemHelper() noexcept
|
||||
{
|
||||
if(fd)
|
||||
{
|
||||
munmap(mem, len);
|
||||
close(fd);
|
||||
shm_unlink(name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int get_fd() const
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
void *get_mem()
|
||||
{
|
||||
return mem;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
int fd = 0;
|
||||
size_t len = 0;
|
||||
void *mem = nullptr;
|
||||
};
|
||||
|
||||
class WaylandDisplayWindow : public DisplayWindow
|
||||
{
|
||||
public:
|
||||
WaylandDisplayWindow(WaylandDisplayBackend* backend, DisplayWindowHost* windowHost, bool popupWindow, WaylandDisplayWindow* owner, RenderAPI renderAPI);
|
||||
~WaylandDisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
void SetWindowFrame(const Rect& box) override;
|
||||
void SetClientFrame(const Rect& box) override;
|
||||
void Show() override;
|
||||
void ShowFullscreen() override;
|
||||
void ShowMaximized() override;
|
||||
void ShowMinimized() override;
|
||||
void ShowNormal() override;
|
||||
void Hide() override;
|
||||
void Activate() override;
|
||||
void ShowCursor(bool enable) override;
|
||||
void LockCursor() override;
|
||||
void UnlockCursor() override;
|
||||
void CaptureMouse() override;
|
||||
void ReleaseMouseCapture() override;
|
||||
void Update() override;
|
||||
bool GetKeyState(InputKey key) override;
|
||||
void SetCursor(StandardCursor cursor) override;
|
||||
|
||||
Rect GetWindowFrame() const override;
|
||||
Size GetClientSize() const override;
|
||||
int GetPixelWidth() const override;
|
||||
int GetPixelHeight() const override;
|
||||
double GetDpiScale() const override;
|
||||
|
||||
void PresentBitmap(int width, int height, const uint32_t* pixels) override;
|
||||
|
||||
void SetBorderColor(uint32_t bgra8) override;
|
||||
void SetCaptionColor(uint32_t bgra8) override;
|
||||
void SetCaptionTextColor(uint32_t bgra8) override;
|
||||
|
||||
std::string GetClipboardText() override;
|
||||
void SetClipboardText(const std::string& text) override;
|
||||
|
||||
Point MapFromGlobal(const Point& pos) const override;
|
||||
Point MapToGlobal(const Point& pos) const override;
|
||||
|
||||
void* GetNativeHandle() override { return (void*)&m_NativeHandle; }
|
||||
|
||||
std::vector<std::string> GetVulkanInstanceExtensions() override;
|
||||
VkSurfaceKHR CreateVulkanSurface(VkInstance instance) override;
|
||||
|
||||
wayland::surface_t GetWindowSurface() { return m_AppSurface; }
|
||||
|
||||
bool IsWindowFullscreen() override;
|
||||
|
||||
private:
|
||||
// Event handlers as otherwise linking DisplayWindowHost On...() functions with Wayland events directly crashes the app
|
||||
// Alternatively to avoid crashes one can capture by value ([=]) instead of reference ([&])
|
||||
void OnXDGToplevelConfigureEvent(int32_t width, int32_t height);
|
||||
void OnExportHandleEvent(std::string exportedHandle);
|
||||
void OnExitEvent();
|
||||
|
||||
void DrawSurface(uint32_t serial = 0);
|
||||
|
||||
void InitializeToplevel();
|
||||
void InitializePopup();
|
||||
|
||||
WaylandDisplayBackend* backend = nullptr;
|
||||
WaylandDisplayWindow* m_owner = nullptr;
|
||||
DisplayWindowHost* windowHost = nullptr;
|
||||
bool m_PopupWindow = false;
|
||||
|
||||
bool m_NeedsUpdate = true;
|
||||
|
||||
Point m_WindowGlobalPos = Point(0, 0);
|
||||
Size m_WindowSize = Size(0, 0);
|
||||
double m_ScaleFactor = 1.0;
|
||||
|
||||
Point m_SurfaceMousePos = Point(0, 0);
|
||||
|
||||
WaylandNativeHandle m_NativeHandle;
|
||||
RenderAPI m_renderAPI;
|
||||
|
||||
wayland::data_device_t m_DataDevice;
|
||||
wayland::data_source_t m_DataSource;
|
||||
|
||||
wayland::zxdg_toplevel_decoration_v1_t m_XDGToplevelDecoration;
|
||||
|
||||
wayland::fractional_scale_v1_t m_FractionalScale;
|
||||
|
||||
wayland::surface_t m_AppSurface;
|
||||
wayland::buffer_t m_AppSurfaceBuffer;
|
||||
|
||||
wayland::xdg_surface_t m_XDGSurface;
|
||||
wayland::xdg_toplevel_t m_XDGToplevel;
|
||||
wayland::xdg_popup_t m_XDGPopup;
|
||||
|
||||
wayland::zxdg_exported_v2_t m_XDGExported;
|
||||
|
||||
wayland::zwp_locked_pointer_v1_t m_LockedPointer;
|
||||
wayland::zwp_confined_pointer_v1_t m_ConfinedPointer;
|
||||
|
||||
wayland::callback_t m_FrameCallback;
|
||||
|
||||
std::string m_windowID;
|
||||
std::string m_ClipboardContents;
|
||||
|
||||
std::shared_ptr<SharedMemHelper> shared_mem;
|
||||
|
||||
bool isFullscreen = false;
|
||||
|
||||
// Helper functions
|
||||
void CreateBuffers(int32_t width, int32_t height);
|
||||
std::string GetWaylandWindowID();
|
||||
|
||||
friend WaylandDisplayBackend;
|
||||
};
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
#include "wl_fractional_scaling_protocol.hpp"
|
||||
|
||||
using namespace wayland;
|
||||
using namespace wayland::detail;
|
||||
|
||||
const wl_interface* fractional_scale_manager_v1_interface_destroy_request[0] = {
|
||||
};
|
||||
|
||||
const wl_interface* fractional_scale_manager_v1_interface_get_fractional_scale_request[2] = {
|
||||
&fractional_scale_v1_interface,
|
||||
&surface_interface,
|
||||
};
|
||||
|
||||
const wl_message fractional_scale_manager_v1_interface_requests[2] = {
|
||||
{
|
||||
"destroy",
|
||||
"",
|
||||
fractional_scale_manager_v1_interface_destroy_request,
|
||||
},
|
||||
{
|
||||
"get_fractional_scale",
|
||||
"no",
|
||||
fractional_scale_manager_v1_interface_get_fractional_scale_request,
|
||||
},
|
||||
};
|
||||
|
||||
const wl_message fractional_scale_manager_v1_interface_events[0] = {
|
||||
};
|
||||
|
||||
const wl_interface wayland::detail::fractional_scale_manager_v1_interface =
|
||||
{
|
||||
"wp_fractional_scale_manager_v1",
|
||||
1,
|
||||
2,
|
||||
fractional_scale_manager_v1_interface_requests,
|
||||
0,
|
||||
fractional_scale_manager_v1_interface_events,
|
||||
};
|
||||
|
||||
const wl_interface* fractional_scale_v1_interface_destroy_request[0] = {
|
||||
};
|
||||
|
||||
const wl_interface* fractional_scale_v1_interface_preferred_scale_event[1] = {
|
||||
nullptr,
|
||||
};
|
||||
|
||||
const wl_message fractional_scale_v1_interface_requests[1] = {
|
||||
{
|
||||
"destroy",
|
||||
"",
|
||||
fractional_scale_v1_interface_destroy_request,
|
||||
},
|
||||
};
|
||||
|
||||
const wl_message fractional_scale_v1_interface_events[1] = {
|
||||
{
|
||||
"preferred_scale",
|
||||
"u",
|
||||
fractional_scale_v1_interface_preferred_scale_event,
|
||||
},
|
||||
};
|
||||
|
||||
const wl_interface wayland::detail::fractional_scale_v1_interface =
|
||||
{
|
||||
"wp_fractional_scale_v1",
|
||||
1,
|
||||
1,
|
||||
fractional_scale_v1_interface_requests,
|
||||
1,
|
||||
fractional_scale_v1_interface_events,
|
||||
};
|
||||
|
||||
fractional_scale_manager_v1_t::fractional_scale_manager_v1_t(const proxy_t &p)
|
||||
: proxy_t(p)
|
||||
{
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&fractional_scale_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return fractional_scale_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
fractional_scale_manager_v1_t::fractional_scale_manager_v1_t()
|
||||
{
|
||||
set_interface(&fractional_scale_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return fractional_scale_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
fractional_scale_manager_v1_t::fractional_scale_manager_v1_t(wp_fractional_scale_manager_v1 *p, wrapper_type t)
|
||||
: proxy_t(reinterpret_cast<wl_proxy*> (p), t){
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&fractional_scale_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return fractional_scale_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
fractional_scale_manager_v1_t::fractional_scale_manager_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/)
|
||||
: proxy_t(wrapped_proxy, construct_proxy_wrapper_tag()){
|
||||
set_interface(&fractional_scale_manager_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return fractional_scale_manager_v1_t(p); });
|
||||
}
|
||||
|
||||
fractional_scale_manager_v1_t fractional_scale_manager_v1_t::proxy_create_wrapper()
|
||||
{
|
||||
return {*this, construct_proxy_wrapper_tag()};
|
||||
}
|
||||
|
||||
const std::string fractional_scale_manager_v1_t::interface_name = "wp_fractional_scale_manager_v1";
|
||||
|
||||
fractional_scale_manager_v1_t::operator wp_fractional_scale_manager_v1*() const
|
||||
{
|
||||
return reinterpret_cast<wp_fractional_scale_manager_v1*> (c_ptr());
|
||||
}
|
||||
|
||||
fractional_scale_v1_t fractional_scale_manager_v1_t::get_fractional_scale(surface_t const& surface)
|
||||
{
|
||||
proxy_t p = marshal_constructor(1U, &fractional_scale_v1_interface, nullptr, surface.proxy_has_object() ? reinterpret_cast<wl_object*>(surface.c_ptr()) : nullptr);
|
||||
return fractional_scale_v1_t(p);
|
||||
}
|
||||
|
||||
|
||||
int fractional_scale_manager_v1_t::dispatcher(uint32_t opcode, const std::vector<any>& args, const std::shared_ptr<detail::events_base_t>& e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
fractional_scale_v1_t::fractional_scale_v1_t(const proxy_t &p)
|
||||
: proxy_t(p)
|
||||
{
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&fractional_scale_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return fractional_scale_v1_t(p); });
|
||||
}
|
||||
|
||||
fractional_scale_v1_t::fractional_scale_v1_t()
|
||||
{
|
||||
set_interface(&fractional_scale_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return fractional_scale_v1_t(p); });
|
||||
}
|
||||
|
||||
fractional_scale_v1_t::fractional_scale_v1_t(wp_fractional_scale_v1 *p, wrapper_type t)
|
||||
: proxy_t(reinterpret_cast<wl_proxy*> (p), t){
|
||||
if(proxy_has_object() && get_wrapper_type() == wrapper_type::standard)
|
||||
{
|
||||
set_events(std::shared_ptr<detail::events_base_t>(new events_t), dispatcher);
|
||||
set_destroy_opcode(0U);
|
||||
}
|
||||
set_interface(&fractional_scale_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return fractional_scale_v1_t(p); });
|
||||
}
|
||||
|
||||
fractional_scale_v1_t::fractional_scale_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/)
|
||||
: proxy_t(wrapped_proxy, construct_proxy_wrapper_tag()){
|
||||
set_interface(&fractional_scale_v1_interface);
|
||||
set_copy_constructor([] (const proxy_t &p) -> proxy_t
|
||||
{ return fractional_scale_v1_t(p); });
|
||||
}
|
||||
|
||||
fractional_scale_v1_t fractional_scale_v1_t::proxy_create_wrapper()
|
||||
{
|
||||
return {*this, construct_proxy_wrapper_tag()};
|
||||
}
|
||||
|
||||
const std::string fractional_scale_v1_t::interface_name = "wp_fractional_scale_v1";
|
||||
|
||||
fractional_scale_v1_t::operator wp_fractional_scale_v1*() const
|
||||
{
|
||||
return reinterpret_cast<wp_fractional_scale_v1*> (c_ptr());
|
||||
}
|
||||
|
||||
std::function<void(uint32_t)> &fractional_scale_v1_t::on_preferred_scale()
|
||||
{
|
||||
return std::static_pointer_cast<events_t>(get_events())->preferred_scale;
|
||||
}
|
||||
|
||||
int fractional_scale_v1_t::dispatcher(uint32_t opcode, const std::vector<any>& args, const std::shared_ptr<detail::events_base_t>& e)
|
||||
{
|
||||
std::shared_ptr<events_t> events = std::static_pointer_cast<events_t>(e);
|
||||
switch(opcode)
|
||||
{
|
||||
case 0:
|
||||
if(events->preferred_scale) events->preferred_scale(args[0].get<uint32_t>());
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wayland-client.hpp>
|
||||
|
||||
struct wp_fractional_scale_manager_v1;
|
||||
struct wp_fractional_scale_v1;
|
||||
|
||||
namespace wayland
|
||||
{
|
||||
class fractional_scale_manager_v1_t;
|
||||
enum class fractional_scale_manager_v1_error : uint32_t;
|
||||
class fractional_scale_v1_t;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
extern const wl_interface fractional_scale_manager_v1_interface;
|
||||
extern const wl_interface fractional_scale_v1_interface;
|
||||
}
|
||||
|
||||
/** \brief fractional surface scale information
|
||||
|
||||
A global interface for requesting surfaces to use fractional scales.
|
||||
|
||||
*/
|
||||
class fractional_scale_manager_v1_t : public proxy_t
|
||||
{
|
||||
private:
|
||||
struct events_t : public detail::events_base_t
|
||||
{
|
||||
};
|
||||
|
||||
static int dispatcher(uint32_t opcode, const std::vector<detail::any>& args, const std::shared_ptr<detail::events_base_t>& e);
|
||||
|
||||
fractional_scale_manager_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/);
|
||||
|
||||
public:
|
||||
fractional_scale_manager_v1_t();
|
||||
explicit fractional_scale_manager_v1_t(const proxy_t &proxy);
|
||||
fractional_scale_manager_v1_t(wp_fractional_scale_manager_v1 *p, wrapper_type t = wrapper_type::standard);
|
||||
|
||||
fractional_scale_manager_v1_t proxy_create_wrapper();
|
||||
|
||||
static const std::string interface_name;
|
||||
|
||||
operator wp_fractional_scale_manager_v1*() const;
|
||||
|
||||
/** \brief extend surface interface for scale information
|
||||
\return the new surface scale info interface id
|
||||
\param surface the surface
|
||||
|
||||
Create an add-on object for the the wl_surface to let the compositor
|
||||
request fractional scales. If the given wl_surface already has a
|
||||
wp_fractional_scale_v1 object associated, the fractional_scale_exists
|
||||
protocol error is raised.
|
||||
|
||||
*/
|
||||
fractional_scale_v1_t get_fractional_scale(surface_t const& surface);
|
||||
|
||||
/** \brief Minimum protocol version required for the \ref get_fractional_scale function
|
||||
*/
|
||||
static constexpr std::uint32_t get_fractional_scale_since_version = 1;
|
||||
|
||||
};
|
||||
|
||||
/** \brief
|
||||
|
||||
*/
|
||||
enum class fractional_scale_manager_v1_error : uint32_t
|
||||
{
|
||||
/** \brief the surface already has a fractional_scale object associated */
|
||||
fractional_scale_exists = 0
|
||||
};
|
||||
|
||||
|
||||
/** \brief fractional scale interface to a wl_surface
|
||||
|
||||
An additional interface to a wl_surface object which allows the compositor
|
||||
to inform the client of the preferred scale.
|
||||
|
||||
*/
|
||||
class fractional_scale_v1_t : public proxy_t
|
||||
{
|
||||
private:
|
||||
struct events_t : public detail::events_base_t
|
||||
{
|
||||
std::function<void(uint32_t)> preferred_scale;
|
||||
};
|
||||
|
||||
static int dispatcher(uint32_t opcode, const std::vector<detail::any>& args, const std::shared_ptr<detail::events_base_t>& e);
|
||||
|
||||
fractional_scale_v1_t(proxy_t const &wrapped_proxy, construct_proxy_wrapper_tag /*unused*/);
|
||||
|
||||
public:
|
||||
fractional_scale_v1_t();
|
||||
explicit fractional_scale_v1_t(const proxy_t &proxy);
|
||||
fractional_scale_v1_t(wp_fractional_scale_v1 *p, wrapper_type t = wrapper_type::standard);
|
||||
|
||||
fractional_scale_v1_t proxy_create_wrapper();
|
||||
|
||||
static const std::string interface_name;
|
||||
|
||||
operator wp_fractional_scale_v1*() const;
|
||||
|
||||
/** \brief notify of new preferred scale
|
||||
\param scale the new preferred scale
|
||||
|
||||
Notification of a new preferred scale for this surface that the
|
||||
compositor suggests that the client should use.
|
||||
|
||||
The sent scale is the numerator of a fraction with a denominator of 120.
|
||||
|
||||
*/
|
||||
std::function<void(uint32_t)> &on_preferred_scale();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
56
libraries/ZWidget/src/window/win32/win32_display_backend.cpp
Normal file
56
libraries/ZWidget/src/window/win32/win32_display_backend.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
#include "win32_display_backend.h"
|
||||
#include "win32_display_window.h"
|
||||
#include "win32_open_file_dialog.h"
|
||||
#include "win32_save_file_dialog.h"
|
||||
#include "win32_open_folder_dialog.h"
|
||||
|
||||
std::unique_ptr<DisplayWindow> Win32DisplayBackend::Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI)
|
||||
{
|
||||
return std::make_unique<Win32DisplayWindow>(windowHost, popupWindow, static_cast<Win32DisplayWindow*>(owner), renderAPI);
|
||||
}
|
||||
|
||||
void Win32DisplayBackend::ProcessEvents()
|
||||
{
|
||||
Win32DisplayWindow::ProcessEvents();
|
||||
}
|
||||
|
||||
void Win32DisplayBackend::RunLoop()
|
||||
{
|
||||
Win32DisplayWindow::RunLoop();
|
||||
}
|
||||
|
||||
void Win32DisplayBackend::ExitLoop()
|
||||
{
|
||||
Win32DisplayWindow::ExitLoop();
|
||||
}
|
||||
|
||||
Size Win32DisplayBackend::GetScreenSize()
|
||||
{
|
||||
return Win32DisplayWindow::GetScreenSize();
|
||||
}
|
||||
|
||||
void* Win32DisplayBackend::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
return Win32DisplayWindow::StartTimer(timeoutMilliseconds, std::move(onTimer));
|
||||
}
|
||||
|
||||
void Win32DisplayBackend::StopTimer(void* timerID)
|
||||
{
|
||||
Win32DisplayWindow::StopTimer(timerID);
|
||||
}
|
||||
|
||||
std::unique_ptr<OpenFileDialog> Win32DisplayBackend::CreateOpenFileDialog(DisplayWindow* owner)
|
||||
{
|
||||
return std::make_unique<Win32OpenFileDialog>(static_cast<Win32DisplayWindow*>(owner));
|
||||
}
|
||||
|
||||
std::unique_ptr<SaveFileDialog> Win32DisplayBackend::CreateSaveFileDialog(DisplayWindow* owner)
|
||||
{
|
||||
return std::make_unique<Win32SaveFileDialog>(static_cast<Win32DisplayWindow*>(owner));
|
||||
}
|
||||
|
||||
std::unique_ptr<OpenFolderDialog> Win32DisplayBackend::CreateOpenFolderDialog(DisplayWindow* owner)
|
||||
{
|
||||
return std::make_unique<Win32OpenFolderDialog>(static_cast<Win32DisplayWindow*>(owner));
|
||||
}
|
||||
23
libraries/ZWidget/src/window/win32/win32_display_backend.h
Normal file
23
libraries/ZWidget/src/window/win32/win32_display_backend.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "window/window.h"
|
||||
|
||||
class Win32DisplayBackend : public DisplayBackend
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI) override;
|
||||
void ProcessEvents() override;
|
||||
void RunLoop() override;
|
||||
void ExitLoop() override;
|
||||
|
||||
void* StartTimer(int timeoutMilliseconds, std::function<void()> onTimer) override;
|
||||
void StopTimer(void* timerID) override;
|
||||
|
||||
Size GetScreenSize() override;
|
||||
|
||||
std::unique_ptr<OpenFileDialog> CreateOpenFileDialog(DisplayWindow* owner) override;
|
||||
std::unique_ptr<SaveFileDialog> CreateSaveFileDialog(DisplayWindow* owner) override;
|
||||
std::unique_ptr<OpenFolderDialog> CreateOpenFolderDialog(DisplayWindow* owner) override;
|
||||
|
||||
bool IsWin32() override { return true; }
|
||||
};
|
||||
788
libraries/ZWidget/src/window/win32/win32_display_window.cpp
Normal file
788
libraries/ZWidget/src/window/win32/win32_display_window.cpp
Normal file
|
|
@ -0,0 +1,788 @@
|
|||
|
||||
#include "win32_display_window.h"
|
||||
#include <windowsx.h>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <dwmapi.h>
|
||||
|
||||
#pragma comment(lib, "dwmapi.lib")
|
||||
|
||||
#ifndef HID_USAGE_PAGE_GENERIC
|
||||
#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
|
||||
#endif
|
||||
|
||||
#ifndef HID_USAGE_GENERIC_MOUSE
|
||||
#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
|
||||
#endif
|
||||
|
||||
#ifndef HID_USAGE_GENERIC_JOYSTICK
|
||||
#define HID_USAGE_GENERIC_JOYSTICK ((USHORT) 0x04)
|
||||
#endif
|
||||
|
||||
#ifndef HID_USAGE_GENERIC_GAMEPAD
|
||||
#define HID_USAGE_GENERIC_GAMEPAD ((USHORT) 0x05)
|
||||
#endif
|
||||
|
||||
#ifndef RIDEV_INPUTSINK
|
||||
#define RIDEV_INPUTSINK (0x100)
|
||||
#endif
|
||||
|
||||
Win32DisplayWindow::Win32DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, Win32DisplayWindow* owner, RenderAPI renderAPI) : WindowHost(windowHost), PopupWindow(popupWindow)
|
||||
{
|
||||
Windows.push_front(this);
|
||||
WindowsIterator = Windows.begin();
|
||||
|
||||
WNDCLASSEX classdesc = {};
|
||||
classdesc.cbSize = sizeof(WNDCLASSEX);
|
||||
classdesc.hInstance = GetModuleHandle(0);
|
||||
classdesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
|
||||
classdesc.lpszClassName = L"ZWidgetWindow";
|
||||
classdesc.lpfnWndProc = &Win32DisplayWindow::WndProc;
|
||||
RegisterClassEx(&classdesc);
|
||||
|
||||
// Microsoft logic at its finest:
|
||||
// WS_EX_DLGMODALFRAME hides the sysmenu icon
|
||||
// WS_CAPTION shows the caption (yay! actually a flag that does what it says it does!)
|
||||
// WS_SYSMENU shows the min/max/close buttons
|
||||
// WS_THICKFRAME makes the window resizable
|
||||
|
||||
DWORD style = 0, exstyle = 0;
|
||||
if (popupWindow)
|
||||
{
|
||||
exstyle = WS_EX_NOACTIVATE;
|
||||
style = WS_POPUP;
|
||||
}
|
||||
else
|
||||
{
|
||||
exstyle = WS_EX_APPWINDOW | WS_EX_DLGMODALFRAME;
|
||||
style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
|
||||
}
|
||||
CreateWindowEx(exstyle, L"ZWidgetWindow", L"", style, 0, 0, 100, 100, owner ? owner->WindowHandle.hwnd : 0, 0, GetModuleHandle(0), this);
|
||||
}
|
||||
|
||||
Win32DisplayWindow::~Win32DisplayWindow()
|
||||
{
|
||||
if (WindowHandle.hwnd)
|
||||
{
|
||||
DestroyWindow(WindowHandle.hwnd);
|
||||
WindowHandle.hwnd = 0;
|
||||
}
|
||||
|
||||
Windows.erase(WindowsIterator);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetWindowTitle(const std::string& text)
|
||||
{
|
||||
SetWindowText(WindowHandle.hwnd, to_utf16(text).c_str());
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetBorderColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle.hwnd, 34/*DWMWA_BORDER_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetCaptionColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle.hwnd, 35/*DWMWA_CAPTION_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetCaptionTextColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle.hwnd, 36/*DWMWA_TEXT_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetWindowFrame(const Rect& box)
|
||||
{
|
||||
double dpiscale = GetDpiScale();
|
||||
SetWindowPos(WindowHandle.hwnd, nullptr, (int)std::round(box.x * dpiscale), (int)std::round(box.y * dpiscale), (int)std::round(box.width * dpiscale), (int)std::round(box.height * dpiscale), SWP_NOACTIVATE | SWP_NOZORDER);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetClientFrame(const Rect& box)
|
||||
{
|
||||
double dpiscale = GetDpiScale();
|
||||
|
||||
RECT rect = {};
|
||||
rect.left = (int)std::round(box.x * dpiscale);
|
||||
rect.top = (int)std::round(box.y * dpiscale);
|
||||
rect.right = rect.left + (int)std::round(box.width * dpiscale);
|
||||
rect.bottom = rect.top + (int)std::round(box.height * dpiscale);
|
||||
|
||||
DWORD style = (DWORD)GetWindowLongPtr(WindowHandle.hwnd, GWL_STYLE);
|
||||
DWORD exstyle = (DWORD)GetWindowLongPtr(WindowHandle.hwnd, GWL_EXSTYLE);
|
||||
AdjustWindowRectExForDpi(&rect, style, FALSE, exstyle, GetDpiForWindow(WindowHandle.hwnd));
|
||||
|
||||
SetWindowPos(WindowHandle.hwnd, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::Show()
|
||||
{
|
||||
ShowWindow(WindowHandle.hwnd, PopupWindow ? SW_SHOWNA : SW_SHOW);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowFullscreen()
|
||||
{
|
||||
HDC screenDC = GetDC(0);
|
||||
int width = GetDeviceCaps(screenDC, HORZRES);
|
||||
int height = GetDeviceCaps(screenDC, VERTRES);
|
||||
ReleaseDC(0, screenDC);
|
||||
DWORD dwStyle = GetWindowLong(WindowHandle.hwnd, GWL_STYLE);
|
||||
SetWindowLongPtr(WindowHandle.hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW);
|
||||
SetWindowLongPtr(WindowHandle.hwnd, GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW);
|
||||
SetWindowPos(WindowHandle.hwnd, HWND_TOP, 0, 0, width, height, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
||||
Fullscreen = true;
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowMaximized()
|
||||
{
|
||||
ShowWindow(WindowHandle.hwnd, SW_SHOWMAXIMIZED);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowMinimized()
|
||||
{
|
||||
ShowWindow(WindowHandle.hwnd, SW_SHOWMINIMIZED);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowNormal()
|
||||
{
|
||||
if (Fullscreen)
|
||||
{
|
||||
SetWindowLongPtr(WindowHandle.hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
|
||||
Fullscreen = false;
|
||||
}
|
||||
ShowWindow(WindowHandle.hwnd, SW_NORMAL);
|
||||
}
|
||||
|
||||
bool Win32DisplayWindow::IsWindowFullscreen()
|
||||
{
|
||||
return Fullscreen;
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::Hide()
|
||||
{
|
||||
ShowWindow(WindowHandle.hwnd, SW_HIDE);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::Activate()
|
||||
{
|
||||
if (!PopupWindow)
|
||||
SetFocus(WindowHandle.hwnd);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowCursor(bool enable)
|
||||
{
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::LockCursor()
|
||||
{
|
||||
if (!MouseLocked)
|
||||
{
|
||||
MouseLocked = true;
|
||||
GetCursorPos(&MouseLockPos);
|
||||
::ShowCursor(FALSE);
|
||||
|
||||
RAWINPUTDEVICE rid = {};
|
||||
rid.usUsagePage = HID_USAGE_PAGE_GENERIC;
|
||||
rid.usUsage = HID_USAGE_GENERIC_MOUSE;
|
||||
rid.dwFlags = RIDEV_INPUTSINK;
|
||||
rid.hwndTarget = WindowHandle.hwnd;
|
||||
RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE));
|
||||
}
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::UnlockCursor()
|
||||
{
|
||||
if (MouseLocked)
|
||||
{
|
||||
RAWINPUTDEVICE rid = {};
|
||||
rid.usUsagePage = HID_USAGE_PAGE_GENERIC;
|
||||
rid.usUsage = HID_USAGE_GENERIC_MOUSE;
|
||||
rid.dwFlags = RIDEV_REMOVE;
|
||||
rid.hwndTarget = 0;
|
||||
RegisterRawInputDevices(&rid, 1, sizeof(rid));
|
||||
|
||||
MouseLocked = false;
|
||||
SetCursorPos(MouseLockPos.x, MouseLockPos.y);
|
||||
::ShowCursor(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::CaptureMouse()
|
||||
{
|
||||
SetCapture(WindowHandle.hwnd);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ReleaseMouseCapture()
|
||||
{
|
||||
ReleaseCapture();
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::Update()
|
||||
{
|
||||
InvalidateRect(WindowHandle.hwnd, nullptr, FALSE);
|
||||
}
|
||||
|
||||
bool Win32DisplayWindow::GetKeyState(InputKey key)
|
||||
{
|
||||
return ::GetKeyState((int)key) & 0x8000; // High bit (0x8000) means key is down, Low bit (0x0001) means key is sticky on (like Caps Lock, Num Lock, etc.)
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetCursor(StandardCursor cursor)
|
||||
{
|
||||
if (cursor != CurrentCursor)
|
||||
{
|
||||
CurrentCursor = cursor;
|
||||
UpdateCursor();
|
||||
}
|
||||
}
|
||||
|
||||
Rect Win32DisplayWindow::GetWindowFrame() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetWindowRect(WindowHandle.hwnd, &box);
|
||||
double dpiscale = GetDpiScale();
|
||||
return Rect(box.left / dpiscale, box.top / dpiscale, (box.right - box.left) / dpiscale, (box.bottom - box.top) / dpiscale);
|
||||
}
|
||||
|
||||
Point Win32DisplayWindow::MapFromGlobal(const Point& pos) const
|
||||
{
|
||||
double dpiscale = GetDpiScale();
|
||||
POINT point = {};
|
||||
point.x = (LONG)std::round(pos.x / dpiscale);
|
||||
point.y = (LONG)std::round(pos.y / dpiscale);
|
||||
ScreenToClient(WindowHandle.hwnd, &point);
|
||||
return Point(point.x * dpiscale, point.y * dpiscale);
|
||||
}
|
||||
|
||||
Point Win32DisplayWindow::MapToGlobal(const Point& pos) const
|
||||
{
|
||||
double dpiscale = GetDpiScale();
|
||||
POINT point = {};
|
||||
point.x = (LONG)std::round(pos.x * dpiscale);
|
||||
point.y = (LONG)std::round(pos.y * dpiscale);
|
||||
ClientToScreen(WindowHandle.hwnd, &point);
|
||||
return Point(point.x / dpiscale, point.y / dpiscale);
|
||||
}
|
||||
|
||||
Size Win32DisplayWindow::GetClientSize() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle.hwnd, &box);
|
||||
double dpiscale = GetDpiScale();
|
||||
return Size(box.right / dpiscale, box.bottom / dpiscale);
|
||||
}
|
||||
|
||||
int Win32DisplayWindow::GetPixelWidth() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle.hwnd, &box);
|
||||
return box.right;
|
||||
}
|
||||
|
||||
int Win32DisplayWindow::GetPixelHeight() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle.hwnd, &box);
|
||||
return box.bottom;
|
||||
}
|
||||
|
||||
double Win32DisplayWindow::GetDpiScale() const
|
||||
{
|
||||
return GetDpiForWindow(WindowHandle.hwnd) / 96.0;
|
||||
}
|
||||
|
||||
std::string Win32DisplayWindow::GetClipboardText()
|
||||
{
|
||||
BOOL result = OpenClipboard(WindowHandle.hwnd);
|
||||
if (result == FALSE)
|
||||
throw std::runtime_error("Unable to open clipboard");
|
||||
|
||||
HANDLE handle = GetClipboardData(CF_UNICODETEXT);
|
||||
if (handle == 0)
|
||||
{
|
||||
CloseClipboard();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::wstring::value_type* data = (std::wstring::value_type*)GlobalLock(handle);
|
||||
if (data == 0)
|
||||
{
|
||||
CloseClipboard();
|
||||
return std::string();
|
||||
}
|
||||
std::string str = from_utf16(data);
|
||||
GlobalUnlock(handle);
|
||||
|
||||
CloseClipboard();
|
||||
return str;
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetClipboardText(const std::string& text)
|
||||
{
|
||||
std::wstring text16 = to_utf16(text);
|
||||
|
||||
BOOL result = OpenClipboard(WindowHandle.hwnd);
|
||||
if (result == FALSE)
|
||||
throw std::runtime_error("Unable to open clipboard");
|
||||
|
||||
result = EmptyClipboard();
|
||||
if (result == FALSE)
|
||||
{
|
||||
CloseClipboard();
|
||||
throw std::runtime_error("Unable to empty clipboard");
|
||||
}
|
||||
|
||||
unsigned int length = (unsigned int)((text16.length() + 1) * sizeof(std::wstring::value_type));
|
||||
HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, length);
|
||||
if (handle == 0)
|
||||
{
|
||||
CloseClipboard();
|
||||
throw std::runtime_error("Unable to allocate clipboard memory");
|
||||
}
|
||||
|
||||
void* data = GlobalLock(handle);
|
||||
if (data == 0)
|
||||
{
|
||||
GlobalFree(handle);
|
||||
CloseClipboard();
|
||||
throw std::runtime_error("Unable to lock clipboard memory");
|
||||
}
|
||||
memcpy(data, text16.c_str(), length);
|
||||
GlobalUnlock(handle);
|
||||
|
||||
HANDLE data_result = SetClipboardData(CF_UNICODETEXT, handle);
|
||||
|
||||
if (data_result == 0)
|
||||
{
|
||||
GlobalFree(handle);
|
||||
CloseClipboard();
|
||||
throw std::runtime_error("Unable to set clipboard data");
|
||||
}
|
||||
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::PresentBitmap(int width, int height, const uint32_t* pixels)
|
||||
{
|
||||
BITMAPV5HEADER header = {};
|
||||
header.bV5Size = sizeof(BITMAPV5HEADER);
|
||||
header.bV5Width = width;
|
||||
header.bV5Height = -height;
|
||||
header.bV5Planes = 1;
|
||||
header.bV5BitCount = 32;
|
||||
header.bV5Compression = BI_BITFIELDS;
|
||||
header.bV5AlphaMask = 0xff000000;
|
||||
header.bV5RedMask = 0x00ff0000;
|
||||
header.bV5GreenMask = 0x0000ff00;
|
||||
header.bV5BlueMask = 0x000000ff;
|
||||
header.bV5SizeImage = width * height * sizeof(uint32_t);
|
||||
header.bV5CSType = LCS_sRGB;
|
||||
|
||||
HDC dc = PaintDC;
|
||||
if (dc != 0)
|
||||
{
|
||||
SetDIBitsToDevice(dc, 0, 0, width, height, 0, 0, 0, height, pixels, (const BITMAPINFO*)&header, BI_RGB);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT Win32DisplayWindow::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
LPARAM result = 0;
|
||||
if (DwmDefWindowProc(WindowHandle.hwnd, msg, wparam, lparam, &result))
|
||||
return result;
|
||||
|
||||
if (msg == WM_INPUT)
|
||||
{
|
||||
bool hasFocus = GetFocus() != 0;
|
||||
|
||||
HRAWINPUT handle = (HRAWINPUT)lparam;
|
||||
UINT size = 0;
|
||||
UINT result = GetRawInputData(handle, RID_INPUT, 0, &size, sizeof(RAWINPUTHEADER));
|
||||
if (result == 0 && size > 0)
|
||||
{
|
||||
size *= 2;
|
||||
std::vector<uint8_t*> buffer(size);
|
||||
result = GetRawInputData(handle, RID_INPUT, buffer.data(), &size, sizeof(RAWINPUTHEADER));
|
||||
if (result >= 0)
|
||||
{
|
||||
RAWINPUT* rawinput = (RAWINPUT*)buffer.data();
|
||||
if (rawinput->header.dwType == RIM_TYPEMOUSE)
|
||||
{
|
||||
if (hasFocus)
|
||||
WindowHost->OnWindowRawMouseMove(rawinput->data.mouse.lLastX, rawinput->data.mouse.lLastY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return DefWindowProc(WindowHandle.hwnd, msg, wparam, lparam);
|
||||
}
|
||||
else if (msg == WM_PAINT)
|
||||
{
|
||||
PAINTSTRUCT paintStruct = {};
|
||||
PaintDC = BeginPaint(WindowHandle.hwnd, &paintStruct);
|
||||
if (PaintDC)
|
||||
{
|
||||
WindowHost->OnWindowPaint();
|
||||
EndPaint(WindowHandle.hwnd, &paintStruct);
|
||||
PaintDC = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (msg == WM_ACTIVATE)
|
||||
{
|
||||
WindowHost->OnWindowActivated();
|
||||
}
|
||||
else if (msg == WM_MOUSEACTIVATE)
|
||||
{
|
||||
// We don't want to activate the window on mouse clicks as that changes the focus from the popup owner to the popup itself
|
||||
if (PopupWindow)
|
||||
return MA_NOACTIVATE;
|
||||
}
|
||||
else if (msg == WM_MOUSEMOVE)
|
||||
{
|
||||
if (MouseLocked && GetFocus() != 0)
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle.hwnd, &box);
|
||||
|
||||
POINT center = {};
|
||||
center.x = box.right / 2;
|
||||
center.y = box.bottom / 2;
|
||||
ClientToScreen(WindowHandle.hwnd, ¢er);
|
||||
|
||||
SetCursorPos(center.x, center.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCursor();
|
||||
}
|
||||
|
||||
if (!TrackMouseActive)
|
||||
{
|
||||
TRACKMOUSEEVENT eventTrack = {};
|
||||
eventTrack.cbSize = sizeof(TRACKMOUSEEVENT);
|
||||
eventTrack.hwndTrack = WindowHandle.hwnd;
|
||||
eventTrack.dwFlags = TME_LEAVE;
|
||||
if (TrackMouseEvent(&eventTrack))
|
||||
TrackMouseActive = true;
|
||||
}
|
||||
|
||||
WindowHost->OnWindowMouseMove(GetLParamPos(lparam));
|
||||
}
|
||||
else if (msg == WM_MOUSELEAVE)
|
||||
{
|
||||
TrackMouseActive = false;
|
||||
WindowHost->OnWindowMouseLeave();
|
||||
}
|
||||
else if (msg == WM_LBUTTONDOWN)
|
||||
{
|
||||
WindowHost->OnWindowMouseDown(GetLParamPos(lparam), InputKey::LeftMouse);
|
||||
}
|
||||
else if (msg == WM_LBUTTONDBLCLK)
|
||||
{
|
||||
WindowHost->OnWindowMouseDoubleclick(GetLParamPos(lparam), InputKey::LeftMouse);
|
||||
}
|
||||
else if (msg == WM_LBUTTONUP)
|
||||
{
|
||||
WindowHost->OnWindowMouseUp(GetLParamPos(lparam), InputKey::LeftMouse);
|
||||
}
|
||||
else if (msg == WM_MBUTTONDOWN)
|
||||
{
|
||||
WindowHost->OnWindowMouseDown(GetLParamPos(lparam), InputKey::MiddleMouse);
|
||||
}
|
||||
else if (msg == WM_MBUTTONDBLCLK)
|
||||
{
|
||||
WindowHost->OnWindowMouseDoubleclick(GetLParamPos(lparam), InputKey::MiddleMouse);
|
||||
}
|
||||
else if (msg == WM_MBUTTONUP)
|
||||
{
|
||||
WindowHost->OnWindowMouseUp(GetLParamPos(lparam), InputKey::MiddleMouse);
|
||||
}
|
||||
else if (msg == WM_RBUTTONDOWN)
|
||||
{
|
||||
WindowHost->OnWindowMouseDown(GetLParamPos(lparam), InputKey::RightMouse);
|
||||
}
|
||||
else if (msg == WM_RBUTTONDBLCLK)
|
||||
{
|
||||
WindowHost->OnWindowMouseDoubleclick(GetLParamPos(lparam), InputKey::RightMouse);
|
||||
}
|
||||
else if (msg == WM_RBUTTONUP)
|
||||
{
|
||||
WindowHost->OnWindowMouseUp(GetLParamPos(lparam), InputKey::RightMouse);
|
||||
}
|
||||
else if (msg == WM_MOUSEWHEEL)
|
||||
{
|
||||
double delta = GET_WHEEL_DELTA_WPARAM(wparam) / (double)WHEEL_DELTA;
|
||||
|
||||
// Note: WM_MOUSEWHEEL uses screen coordinates. GetLParamPos assumes client coordinates.
|
||||
double dpiscale = GetDpiScale();
|
||||
POINT pos;
|
||||
pos.x = GET_X_LPARAM(lparam);
|
||||
pos.y = GET_Y_LPARAM(lparam);
|
||||
ScreenToClient(WindowHandle.hwnd, &pos);
|
||||
|
||||
WindowHost->OnWindowMouseWheel(Point(pos.x / dpiscale, pos.y / dpiscale), delta < 0.0 ? InputKey::MouseWheelDown : InputKey::MouseWheelUp);
|
||||
}
|
||||
else if (msg == WM_CHAR)
|
||||
{
|
||||
wchar_t buf[2] = { (wchar_t)wparam, 0 };
|
||||
WindowHost->OnWindowKeyChar(from_utf16(buf));
|
||||
}
|
||||
else if (msg == WM_KEYDOWN)
|
||||
{
|
||||
WindowHost->OnWindowKeyDown((InputKey)wparam);
|
||||
}
|
||||
else if (msg == WM_KEYUP)
|
||||
{
|
||||
WindowHost->OnWindowKeyUp((InputKey)wparam);
|
||||
}
|
||||
else if (msg == WM_SETFOCUS)
|
||||
{
|
||||
if (MouseLocked)
|
||||
{
|
||||
::ShowCursor(FALSE);
|
||||
}
|
||||
}
|
||||
else if (msg == WM_KILLFOCUS)
|
||||
{
|
||||
if (MouseLocked)
|
||||
{
|
||||
::ShowCursor(TRUE);
|
||||
}
|
||||
}
|
||||
else if (msg == WM_CLOSE)
|
||||
{
|
||||
WindowHost->OnWindowClose();
|
||||
return 0;
|
||||
}
|
||||
else if (msg == WM_SIZE)
|
||||
{
|
||||
WindowHost->OnWindowGeometryChanged();
|
||||
return 0;
|
||||
}
|
||||
/*else if (msg == WM_NCCALCSIZE && wparam == TRUE) // calculate client area for the window
|
||||
{
|
||||
NCCALCSIZE_PARAMS* calcsize = (NCCALCSIZE_PARAMS*)lparam;
|
||||
return WVR_REDRAW;
|
||||
}*/
|
||||
|
||||
return DefWindowProc(WindowHandle.hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::UpdateCursor()
|
||||
{
|
||||
LPCWSTR cursor = IDC_ARROW;
|
||||
switch (CurrentCursor)
|
||||
{
|
||||
case StandardCursor::arrow: cursor = IDC_ARROW; break;
|
||||
case StandardCursor::appstarting: cursor = IDC_APPSTARTING; break;
|
||||
case StandardCursor::cross: cursor = IDC_CROSS; break;
|
||||
case StandardCursor::hand: cursor = IDC_HAND; break;
|
||||
case StandardCursor::ibeam: cursor = IDC_IBEAM; break;
|
||||
case StandardCursor::no: cursor = IDC_NO; break;
|
||||
case StandardCursor::size_all: cursor = IDC_SIZEALL; break;
|
||||
case StandardCursor::size_nesw: cursor = IDC_SIZENESW; break;
|
||||
case StandardCursor::size_ns: cursor = IDC_SIZENS; break;
|
||||
case StandardCursor::size_nwse: cursor = IDC_SIZENWSE; break;
|
||||
case StandardCursor::size_we: cursor = IDC_SIZEWE; break;
|
||||
case StandardCursor::uparrow: cursor = IDC_UPARROW; break;
|
||||
case StandardCursor::wait: cursor = IDC_WAIT; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
::SetCursor((HCURSOR)LoadImage(0, cursor, IMAGE_CURSOR, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_SHARED));
|
||||
}
|
||||
|
||||
Point Win32DisplayWindow::GetLParamPos(LPARAM lparam) const
|
||||
{
|
||||
double dpiscale = GetDpiScale();
|
||||
return Point(GET_X_LPARAM(lparam) / dpiscale, GET_Y_LPARAM(lparam) / dpiscale);
|
||||
}
|
||||
|
||||
LRESULT Win32DisplayWindow::WndProc(HWND windowhandle, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
if (msg == WM_CREATE)
|
||||
{
|
||||
CREATESTRUCT* createstruct = (CREATESTRUCT*)lparam;
|
||||
Win32DisplayWindow* viewport = (Win32DisplayWindow*)createstruct->lpCreateParams;
|
||||
viewport->WindowHandle.hwnd = windowhandle;
|
||||
SetWindowLongPtr(windowhandle, GWLP_USERDATA, (LONG_PTR)viewport);
|
||||
return viewport->OnWindowMessage(msg, wparam, lparam);
|
||||
}
|
||||
else
|
||||
{
|
||||
Win32DisplayWindow* viewport = (Win32DisplayWindow*)GetWindowLongPtr(windowhandle, GWLP_USERDATA);
|
||||
if (viewport)
|
||||
{
|
||||
LRESULT result = viewport->OnWindowMessage(msg, wparam, lparam);
|
||||
if (msg == WM_DESTROY)
|
||||
{
|
||||
SetWindowLongPtr(windowhandle, GWLP_USERDATA, 0);
|
||||
viewport->WindowHandle.hwnd = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DefWindowProc(windowhandle, msg, wparam, lparam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ProcessEvents()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
MSG msg = {};
|
||||
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) <= 0)
|
||||
break;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::RunLoop()
|
||||
{
|
||||
while (!ExitRunLoop && !Windows.empty())
|
||||
{
|
||||
MSG msg = {};
|
||||
if (GetMessage(&msg, 0, 0, 0) <= 0)
|
||||
break;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
ExitRunLoop = false;
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ExitLoop()
|
||||
{
|
||||
ExitRunLoop = true;
|
||||
}
|
||||
|
||||
Size Win32DisplayWindow::GetScreenSize()
|
||||
{
|
||||
HDC screenDC = GetDC(0);
|
||||
int screenWidth = GetDeviceCaps(screenDC, HORZRES);
|
||||
int screenHeight = GetDeviceCaps(screenDC, VERTRES);
|
||||
double dpiScale = GetDeviceCaps(screenDC, LOGPIXELSX) / 96.0;
|
||||
ReleaseDC(0, screenDC);
|
||||
|
||||
return Size(screenWidth / dpiScale, screenHeight / dpiScale);
|
||||
}
|
||||
|
||||
// This is to avoid needing all the Vulkan headers and the volk binding library just for this:
|
||||
#ifndef VK_VERSION_1_0
|
||||
|
||||
#define VKAPI_CALL __stdcall
|
||||
#define VKAPI_PTR VKAPI_CALL
|
||||
|
||||
typedef uint32_t VkFlags;
|
||||
typedef enum VkStructureType { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType;
|
||||
typedef enum VkResult { VK_SUCCESS = 0, VK_RESULT_MAX_ENUM = 0x7FFFFFFF } VkResult;
|
||||
typedef struct VkAllocationCallbacks VkAllocationCallbacks;
|
||||
|
||||
typedef void (VKAPI_PTR* PFN_vkVoidFunction)(void);
|
||||
typedef PFN_vkVoidFunction(VKAPI_PTR* PFN_vkGetInstanceProcAddr)(VkInstance instance, const char* pName);
|
||||
|
||||
#ifndef VK_KHR_win32_surface
|
||||
|
||||
typedef VkFlags VkWin32SurfaceCreateFlagsKHR;
|
||||
typedef struct VkWin32SurfaceCreateInfoKHR
|
||||
{
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkWin32SurfaceCreateFlagsKHR flags;
|
||||
HINSTANCE hinstance;
|
||||
HWND hwnd;
|
||||
} VkWin32SurfaceCreateInfoKHR;
|
||||
|
||||
typedef VkResult(VKAPI_PTR* PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class ZWidgetVulkanLoader
|
||||
{
|
||||
public:
|
||||
ZWidgetVulkanLoader()
|
||||
{
|
||||
module = LoadLibraryA("vulkan-1.dll");
|
||||
if (!module)
|
||||
throw std::runtime_error("Could not load vulkan-1.dll");
|
||||
|
||||
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)(void(*)(void))GetProcAddress(module, "vkGetInstanceProcAddr");
|
||||
if (!vkGetInstanceProcAddr)
|
||||
{
|
||||
FreeLibrary(module);
|
||||
throw std::runtime_error("vkGetInstanceProcAddr not found in vulkan-1.dll");
|
||||
}
|
||||
}
|
||||
|
||||
~ZWidgetVulkanLoader()
|
||||
{
|
||||
FreeLibrary(module);
|
||||
}
|
||||
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = nullptr;
|
||||
HMODULE module = {};
|
||||
};
|
||||
|
||||
VkSurfaceKHR Win32DisplayWindow::CreateVulkanSurface(VkInstance instance)
|
||||
{
|
||||
static ZWidgetVulkanLoader loader;
|
||||
|
||||
auto vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)loader.vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");
|
||||
if (!vkCreateWin32SurfaceKHR)
|
||||
throw std::runtime_error("Could not create vulkan surface");
|
||||
|
||||
VkWin32SurfaceCreateInfoKHR createInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
|
||||
createInfo.hwnd = WindowHandle.hwnd;
|
||||
createInfo.hinstance = GetModuleHandle(nullptr);
|
||||
|
||||
VkSurfaceKHR surface = {};
|
||||
VkResult result = vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &surface);
|
||||
if (result != VK_SUCCESS)
|
||||
throw std::runtime_error("Could not create vulkan surface");
|
||||
return surface;
|
||||
}
|
||||
|
||||
std::vector<std::string> Win32DisplayWindow::GetVulkanInstanceExtensions()
|
||||
{
|
||||
return { "VK_KHR_surface", "VK_KHR_win32_surface" };
|
||||
}
|
||||
|
||||
static void CALLBACK Win32TimerCallback(HWND handle, UINT message, UINT_PTR timerID, DWORD timestamp)
|
||||
{
|
||||
auto it = Win32DisplayWindow::Timers.find(timerID);
|
||||
if (it != Win32DisplayWindow::Timers.end())
|
||||
{
|
||||
auto callback = it->second;
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void* Win32DisplayWindow::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
UINT_PTR result = SetTimer(0, 0, timeoutMilliseconds, Win32TimerCallback);
|
||||
if (result == 0)
|
||||
throw std::runtime_error("Could not create timer");
|
||||
Timers[result] = std::move(onTimer);
|
||||
return (void*)result;
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::StopTimer(void* timerID)
|
||||
{
|
||||
auto it = Timers.find((UINT_PTR)timerID);
|
||||
if (it != Timers.end())
|
||||
{
|
||||
Timers.erase(it);
|
||||
KillTimer(0, (UINT_PTR)timerID);
|
||||
}
|
||||
}
|
||||
|
||||
std::list<Win32DisplayWindow*> Win32DisplayWindow::Windows;
|
||||
bool Win32DisplayWindow::ExitRunLoop;
|
||||
|
||||
std::unordered_map<UINT_PTR, std::function<void()>> Win32DisplayWindow::Timers;
|
||||
|
|
@ -1,21 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#define NOMINMAX
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#ifndef WINVER
|
||||
#define WINVER 0x0605
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#include "win32_util.h"
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <zwidget/window/window.h>
|
||||
#include <zwidget/window/win32nativehandle.h>
|
||||
|
||||
class Win32Window : public DisplayWindow
|
||||
class Win32DisplayWindow : public DisplayWindow
|
||||
{
|
||||
public:
|
||||
Win32Window(DisplayWindowHost* windowHost);
|
||||
~Win32Window();
|
||||
Win32DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, Win32DisplayWindow* owner, RenderAPI renderAPI);
|
||||
~Win32DisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
void SetWindowFrame(const Rect& box) override;
|
||||
|
|
@ -25,6 +21,7 @@ public:
|
|||
void ShowMaximized() override;
|
||||
void ShowMinimized() override;
|
||||
void ShowNormal() override;
|
||||
bool IsWindowFullscreen() override;
|
||||
void Hide() override;
|
||||
void Activate() override;
|
||||
void ShowCursor(bool enable) override;
|
||||
|
|
@ -33,7 +30,7 @@ public:
|
|||
void CaptureMouse() override;
|
||||
void ReleaseMouseCapture() override;
|
||||
void Update() override;
|
||||
bool GetKeyState(EInputKey key) override;
|
||||
bool GetKeyState(InputKey key) override;
|
||||
|
||||
void SetCursor(StandardCursor cursor) override;
|
||||
void UpdateCursor();
|
||||
|
|
@ -53,8 +50,16 @@ public:
|
|||
std::string GetClipboardText() override;
|
||||
void SetClipboardText(const std::string& text) override;
|
||||
|
||||
Point MapFromGlobal(const Point& pos) const override;
|
||||
Point MapToGlobal(const Point& pos) const override;
|
||||
|
||||
Point GetLParamPos(LPARAM lparam) const;
|
||||
|
||||
void* GetNativeHandle() override { return &WindowHandle; }
|
||||
|
||||
std::vector<std::string> GetVulkanInstanceExtensions() override;
|
||||
VkSurfaceKHR CreateVulkanSurface(VkInstance instance) override;
|
||||
|
||||
static void ProcessEvents();
|
||||
static void RunLoop();
|
||||
static void ExitLoop();
|
||||
|
|
@ -64,8 +69,8 @@ public:
|
|||
static void StopTimer(void* timerID);
|
||||
|
||||
static bool ExitRunLoop;
|
||||
static std::list<Win32Window*> Windows;
|
||||
std::list<Win32Window*>::iterator WindowsIterator;
|
||||
static std::list<Win32DisplayWindow*> Windows;
|
||||
std::list<Win32DisplayWindow*>::iterator WindowsIterator;
|
||||
|
||||
static std::unordered_map<UINT_PTR, std::function<void()>> Timers;
|
||||
|
||||
|
|
@ -73,13 +78,16 @@ public:
|
|||
static LRESULT CALLBACK WndProc(HWND windowhandle, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
|
||||
DisplayWindowHost* WindowHost = nullptr;
|
||||
bool PopupWindow = false;
|
||||
|
||||
HWND WindowHandle = 0;
|
||||
Win32NativeHandle WindowHandle;
|
||||
bool Fullscreen = false;
|
||||
|
||||
bool MouseLocked = false;
|
||||
POINT MouseLockPos = {};
|
||||
|
||||
bool TrackMouseActive = false;
|
||||
|
||||
HDC PaintDC = 0;
|
||||
|
||||
StandardCursor CurrentCursor = StandardCursor::arrow;
|
||||
227
libraries/ZWidget/src/window/win32/win32_open_file_dialog.cpp
Normal file
227
libraries/ZWidget/src/window/win32/win32_open_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
|
||||
#include "win32_open_file_dialog.h"
|
||||
#include "win32_display_window.h"
|
||||
#include "core/widget.h"
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
|
||||
Win32OpenFileDialog::Win32OpenFileDialog(Win32DisplayWindow* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
bool Win32OpenFileDialog::Show()
|
||||
{
|
||||
std::wstring title16 = to_utf16(title);
|
||||
std::wstring initial_directory16 = to_utf16(initial_directory);
|
||||
|
||||
HRESULT result;
|
||||
ComPtr<IFileOpenDialog> open_dialog;
|
||||
|
||||
result = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, open_dialog.GetIID(), open_dialog.InitPtr());
|
||||
throw_if_failed(result, "CoCreateInstance(FileOpenDialog) failed");
|
||||
|
||||
result = open_dialog->SetTitle(title16.c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetTitle failed");
|
||||
|
||||
if (!initial_filename.empty())
|
||||
{
|
||||
result = open_dialog->SetFileName(to_utf16(initial_filename).c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileName failed");
|
||||
}
|
||||
|
||||
FILEOPENDIALOGOPTIONS options = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST;
|
||||
if (multi_select)
|
||||
options |= FOS_ALLOWMULTISELECT;
|
||||
result = open_dialog->SetOptions(options);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetOptions() failed");
|
||||
|
||||
if (!filters.empty())
|
||||
{
|
||||
std::vector<COMDLG_FILTERSPEC> filterspecs(filters.size());
|
||||
std::vector<std::wstring> descriptions(filters.size());
|
||||
std::vector<std::wstring> extensions(filters.size());
|
||||
for (size_t i = 0; i < filters.size(); i++)
|
||||
{
|
||||
descriptions[i] = to_utf16(filters[i].description);
|
||||
extensions[i] = to_utf16(filters[i].extension);
|
||||
COMDLG_FILTERSPEC& spec = filterspecs[i];
|
||||
spec.pszName = descriptions[i].c_str();
|
||||
spec.pszSpec = extensions[i].c_str();
|
||||
}
|
||||
result = open_dialog->SetFileTypes((UINT)filterspecs.size(), filterspecs.data());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileTypes() failed");
|
||||
|
||||
if ((size_t)filterindex < filters.size())
|
||||
{
|
||||
result = open_dialog->SetFileTypeIndex(filterindex);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileTypeIndex() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (!defaultext.empty())
|
||||
{
|
||||
result = open_dialog->SetDefaultExtension(to_utf16(defaultext).c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetDefaultExtension() failed");
|
||||
}
|
||||
|
||||
if (initial_directory16.length() > 0)
|
||||
{
|
||||
LPITEMIDLIST item_id_list = nullptr;
|
||||
SFGAOF flags = 0;
|
||||
result = SHParseDisplayName(initial_directory16.c_str(), nullptr, &item_id_list, SFGAO_FILESYSTEM, &flags);
|
||||
throw_if_failed(result, "SHParseDisplayName failed");
|
||||
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateShellItem(nullptr, nullptr, item_id_list, folder_item.TypedInitPtr());
|
||||
ILFree(item_id_list);
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
|
||||
/* This code requires Windows Vista or newer:
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateItemFromParsingName(initial_directory16.c_str(), nullptr, folder_item.GetIID(), folder_item.InitPtr());
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
*/
|
||||
|
||||
if (folder_item)
|
||||
{
|
||||
result = open_dialog->SetFolder(folder_item);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFolder failed");
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (SUCCEEDED(result))
|
||||
{
|
||||
ComPtr<IShellItemArray> items;
|
||||
result = open_dialog->GetResults(items.TypedInitPtr());
|
||||
throw_if_failed(result, "IFileOpenDialog.GetSelectedItems failed");
|
||||
|
||||
DWORD num_items = 0;
|
||||
result = items->GetCount(&num_items);
|
||||
throw_if_failed(result, "IShellItemArray.GetCount failed");
|
||||
|
||||
for (DWORD i = 0; i < num_items; i++)
|
||||
{
|
||||
ComPtr<IShellItem> item;
|
||||
result = items->GetItemAt(i, item.TypedInitPtr());
|
||||
throw_if_failed(result, "IShellItemArray.GetItemAt failed");
|
||||
|
||||
WCHAR* buffer = nullptr;
|
||||
result = item->GetDisplayName(SIGDN_FILESYSPATH, &buffer);
|
||||
throw_if_failed(result, "IShellItem.GetDisplayName failed");
|
||||
|
||||
std::wstring output16;
|
||||
if (buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
output16 = buffer;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CoTaskMemFree(buffer);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CoTaskMemFree(buffer);
|
||||
filenames.push_back(from_utf16(output16));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Win32OpenFileDialog::Filename() const
|
||||
{
|
||||
return !filenames.empty() ? filenames.front() : std::string();
|
||||
}
|
||||
|
||||
std::vector<std::string> Win32OpenFileDialog::Filenames() const
|
||||
{
|
||||
return filenames;
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::SetMultiSelect(bool new_multi_select)
|
||||
{
|
||||
multi_select = new_multi_select;
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::SetFilename(const std::string& filename)
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::AddFilter(const std::string& filter_description, const std::string& filter_extension)
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::ClearFilters()
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::SetFilterIndex(int filter_index)
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::SetDefaultExtension(const std::string& extension)
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
|
||||
void Win32OpenFileDialog::throw_if_failed(HRESULT result, const std::string& error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
44
libraries/ZWidget/src/window/win32/win32_open_file_dialog.h
Normal file
44
libraries/ZWidget/src/window/win32/win32_open_file_dialog.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/open_file_dialog.h"
|
||||
#include "win32_util.h"
|
||||
|
||||
class Win32DisplayWindow;
|
||||
|
||||
class Win32OpenFileDialog : public OpenFileDialog
|
||||
{
|
||||
public:
|
||||
Win32OpenFileDialog(Win32DisplayWindow* owner);
|
||||
|
||||
bool Show() override;
|
||||
std::string Filename() const override;
|
||||
std::vector<std::string> Filenames() const override;
|
||||
void SetMultiSelect(bool new_multi_select) override;
|
||||
void SetFilename(const std::string& filename) override;
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override;
|
||||
void ClearFilters() override;
|
||||
void SetFilterIndex(int filter_index) override;
|
||||
void SetInitialDirectory(const std::string& path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
void SetDefaultExtension(const std::string& extension) override;
|
||||
|
||||
private:
|
||||
void throw_if_failed(HRESULT result, const std::string& error);
|
||||
|
||||
Win32DisplayWindow* owner = nullptr;
|
||||
|
||||
std::string initial_directory;
|
||||
std::string initial_filename;
|
||||
std::string title;
|
||||
std::vector<std::string> filenames;
|
||||
bool multi_select = false;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string description;
|
||||
std::string extension;
|
||||
};
|
||||
std::vector<Filter> filters;
|
||||
int filterindex = 0;
|
||||
std::string defaultext;
|
||||
};
|
||||
111
libraries/ZWidget/src/window/win32/win32_open_folder_dialog.cpp
Normal file
111
libraries/ZWidget/src/window/win32/win32_open_folder_dialog.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
|
||||
#include "win32_open_folder_dialog.h"
|
||||
#include "win32_display_window.h"
|
||||
#include "core/widget.h"
|
||||
#include <stdexcept>
|
||||
|
||||
Win32OpenFolderDialog::Win32OpenFolderDialog(Win32DisplayWindow* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
bool Win32OpenFolderDialog::Show()
|
||||
{
|
||||
std::wstring title16 = to_utf16(title);
|
||||
std::wstring initial_directory16 = to_utf16(initial_directory);
|
||||
|
||||
HRESULT result;
|
||||
ComPtr<IFileOpenDialog> open_dialog;
|
||||
|
||||
result = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, open_dialog.GetIID(), open_dialog.InitPtr());
|
||||
throw_if_failed(result, "CoCreateInstance(FileOpenDialog) failed");
|
||||
|
||||
result = open_dialog->SetTitle(title16.c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetTitle failed");
|
||||
|
||||
result = open_dialog->SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetOptions((FOS_PICKFOLDERS) failed");
|
||||
|
||||
if (initial_directory16.length() > 0)
|
||||
{
|
||||
LPITEMIDLIST item_id_list = nullptr;
|
||||
SFGAOF flags = 0;
|
||||
result = SHParseDisplayName(initial_directory16.c_str(), nullptr, &item_id_list, SFGAO_FILESYSTEM, &flags);
|
||||
throw_if_failed(result, "SHParseDisplayName failed");
|
||||
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateShellItem(nullptr, nullptr, item_id_list, folder_item.TypedInitPtr());
|
||||
ILFree(item_id_list);
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
|
||||
/* This code requires Windows Vista or newer:
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateItemFromParsingName(initial_directory16.c_str(), nullptr, folder_item.GetIID(), folder_item.InitPtr());
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
*/
|
||||
|
||||
if (folder_item)
|
||||
{
|
||||
result = open_dialog->SetFolder(folder_item);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFolder failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (owner)
|
||||
result = open_dialog->Show(owner->WindowHandle.hwnd);
|
||||
else
|
||||
result = open_dialog->Show(0);
|
||||
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
ComPtr<IShellItem> chosen_folder;
|
||||
result = open_dialog->GetResult(chosen_folder.TypedInitPtr());
|
||||
throw_if_failed(result, "IFileOpenDialog.GetResult failed");
|
||||
|
||||
WCHAR* buffer = nullptr;
|
||||
result = chosen_folder->GetDisplayName(SIGDN_FILESYSPATH, &buffer);
|
||||
throw_if_failed(result, "IShellItem.GetDisplayName failed");
|
||||
|
||||
std::wstring output_directory16;
|
||||
if (buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
output_directory16 = buffer;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CoTaskMemFree(buffer);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CoTaskMemFree(buffer);
|
||||
selected_path = from_utf16(output_directory16);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Win32OpenFolderDialog::SelectedPath() const
|
||||
{
|
||||
return selected_path;
|
||||
}
|
||||
|
||||
void Win32OpenFolderDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void Win32OpenFolderDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void Win32OpenFolderDialog::throw_if_failed(HRESULT result, const std::string& error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/open_folder_dialog.h"
|
||||
#include "win32_util.h"
|
||||
|
||||
class Win32DisplayWindow;
|
||||
|
||||
class Win32OpenFolderDialog : public OpenFolderDialog
|
||||
{
|
||||
public:
|
||||
Win32OpenFolderDialog(Win32DisplayWindow* owner);
|
||||
|
||||
bool Show() override;
|
||||
std::string SelectedPath() const override;
|
||||
void SetInitialDirectory(const std::string& path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
|
||||
private:
|
||||
void throw_if_failed(HRESULT result, const std::string& error);
|
||||
|
||||
Win32DisplayWindow* owner = nullptr;
|
||||
|
||||
std::string selected_path;
|
||||
std::string initial_directory;
|
||||
std::string title;
|
||||
};
|
||||
174
libraries/ZWidget/src/window/win32/win32_save_file_dialog.cpp
Normal file
174
libraries/ZWidget/src/window/win32/win32_save_file_dialog.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
|
||||
#include "win32_save_file_dialog.h"
|
||||
#include "win32_display_window.h"
|
||||
#include "core/widget.h"
|
||||
|
||||
Win32SaveFileDialog::Win32SaveFileDialog(Win32DisplayWindow* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
bool Win32SaveFileDialog::Show()
|
||||
{
|
||||
std::wstring title16 = to_utf16(title);
|
||||
std::wstring initial_directory16 = to_utf16(initial_directory);
|
||||
|
||||
HRESULT result;
|
||||
ComPtr<IFileSaveDialog> save_dialog;
|
||||
|
||||
result = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_ALL, save_dialog.GetIID(), save_dialog.InitPtr());
|
||||
throw_if_failed(result, "CoCreateInstance(FileSaveDialog) failed");
|
||||
|
||||
result = save_dialog->SetTitle(title16.c_str());
|
||||
throw_if_failed(result, "IFileSaveDialog.SetTitle failed");
|
||||
|
||||
if (!initial_filename.empty())
|
||||
{
|
||||
result = save_dialog->SetFileName(to_utf16(initial_filename).c_str());
|
||||
throw_if_failed(result, "IFileSaveDialog.SetFileName failed");
|
||||
}
|
||||
|
||||
FILEOPENDIALOGOPTIONS options = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST;
|
||||
result = save_dialog->SetOptions(options);
|
||||
throw_if_failed(result, "IFileSaveDialog.SetOptions() failed");
|
||||
|
||||
if (!filters.empty())
|
||||
{
|
||||
std::vector<COMDLG_FILTERSPEC> filterspecs(filters.size());
|
||||
std::vector<std::wstring> descriptions(filters.size());
|
||||
std::vector<std::wstring> extensions(filters.size());
|
||||
for (size_t i = 0; i < filters.size(); i++)
|
||||
{
|
||||
descriptions[i] = to_utf16(filters[i].description);
|
||||
extensions[i] = to_utf16(filters[i].extension);
|
||||
COMDLG_FILTERSPEC& spec = filterspecs[i];
|
||||
spec.pszName = descriptions[i].c_str();
|
||||
spec.pszSpec = extensions[i].c_str();
|
||||
}
|
||||
result = save_dialog->SetFileTypes((UINT)filterspecs.size(), filterspecs.data());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileTypes() failed");
|
||||
|
||||
if ((size_t)filterindex < filters.size())
|
||||
{
|
||||
result = save_dialog->SetFileTypeIndex(filterindex);
|
||||
throw_if_failed(result, "IFileOpenDialog.SetFileTypeIndex() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (!defaultext.empty())
|
||||
{
|
||||
result = save_dialog->SetDefaultExtension(to_utf16(defaultext).c_str());
|
||||
throw_if_failed(result, "IFileOpenDialog.SetDefaultExtension() failed");
|
||||
}
|
||||
|
||||
if (initial_directory16.length() > 0)
|
||||
{
|
||||
LPITEMIDLIST item_id_list = nullptr;
|
||||
SFGAOF flags = 0;
|
||||
result = SHParseDisplayName(initial_directory16.c_str(), nullptr, &item_id_list, SFGAO_FILESYSTEM, &flags);
|
||||
throw_if_failed(result, "SHParseDisplayName failed");
|
||||
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateShellItem(nullptr, nullptr, item_id_list, folder_item.TypedInitPtr());
|
||||
ILFree(item_id_list);
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
|
||||
/* This code requires Windows Vista or newer:
|
||||
ComPtr<IShellItem> folder_item;
|
||||
result = SHCreateItemFromParsingName(initial_directory16.c_str(), nullptr, folder_item.GetIID(), folder_item.InitPtr());
|
||||
throw_if_failed(result, "SHCreateItemFromParsingName failed");
|
||||
*/
|
||||
|
||||
if (folder_item)
|
||||
{
|
||||
result = save_dialog->SetFolder(folder_item);
|
||||
throw_if_failed(result, "IFileSaveDialog.SetFolder failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (owner)
|
||||
result = save_dialog->Show(owner->WindowHandle.hwnd);
|
||||
else
|
||||
result = save_dialog->Show(0);
|
||||
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
ComPtr<IShellItem> chosen_folder;
|
||||
result = save_dialog->GetResult(chosen_folder.TypedInitPtr());
|
||||
throw_if_failed(result, "IFileSaveDialog.GetResult failed");
|
||||
|
||||
WCHAR* buffer = nullptr;
|
||||
result = chosen_folder->GetDisplayName(SIGDN_FILESYSPATH, &buffer);
|
||||
throw_if_failed(result, "IShellItem.GetDisplayName failed");
|
||||
|
||||
std::wstring output16;
|
||||
if (buffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
output16 = buffer;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CoTaskMemFree(buffer);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CoTaskMemFree(buffer);
|
||||
filename = from_utf16(output16);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Win32SaveFileDialog::Filename() const
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
void Win32SaveFileDialog::SetFilename(const std::string& filename)
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void Win32SaveFileDialog::AddFilter(const std::string& filter_description, const std::string& filter_extension)
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void Win32SaveFileDialog::ClearFilters()
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void Win32SaveFileDialog::SetFilterIndex(int filter_index)
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void Win32SaveFileDialog::SetInitialDirectory(const std::string& path)
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void Win32SaveFileDialog::SetTitle(const std::string& newtitle)
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void Win32SaveFileDialog::SetDefaultExtension(const std::string& extension)
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
|
||||
void Win32SaveFileDialog::throw_if_failed(HRESULT result, const std::string& error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
42
libraries/ZWidget/src/window/win32/win32_save_file_dialog.h
Normal file
42
libraries/ZWidget/src/window/win32/win32_save_file_dialog.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemdialogs/save_file_dialog.h"
|
||||
#include "win32_util.h"
|
||||
|
||||
class Win32DisplayWindow;
|
||||
|
||||
class Win32SaveFileDialog : public SaveFileDialog
|
||||
{
|
||||
public:
|
||||
Win32SaveFileDialog(Win32DisplayWindow* owner);
|
||||
|
||||
bool Show() override;
|
||||
std::string Filename() const override;
|
||||
void SetFilename(const std::string& filename) override;
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override;
|
||||
void ClearFilters() override;
|
||||
void SetFilterIndex(int filter_index) override;
|
||||
void SetInitialDirectory(const std::string& path) override;
|
||||
void SetTitle(const std::string& newtitle) override;
|
||||
void SetDefaultExtension(const std::string& extension) override;
|
||||
|
||||
private:
|
||||
void throw_if_failed(HRESULT result, const std::string& error);
|
||||
|
||||
Win32DisplayWindow* owner = nullptr;
|
||||
|
||||
std::string filename;
|
||||
std::string initial_directory;
|
||||
std::string initial_filename;
|
||||
std::string title;
|
||||
std::vector<std::string> filenames;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
std::string description;
|
||||
std::string extension;
|
||||
};
|
||||
std::vector<Filter> filters;
|
||||
int filterindex = 0;
|
||||
std::string defaultext;
|
||||
};
|
||||
60
libraries/ZWidget/src/window/win32/win32_util.h
Normal file
60
libraries/ZWidget/src/window/win32/win32_util.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#define NOMINMAX
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#ifndef WINVER
|
||||
#define WINVER 0x0605
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#include <Shlobj.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::string from_utf16(const std::wstring& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
std::string result;
|
||||
result.resize(needed);
|
||||
needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size(), nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::wstring to_utf16(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
std::wstring result;
|
||||
result.resize(needed);
|
||||
needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size());
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class ComPtr
|
||||
{
|
||||
public:
|
||||
ComPtr() { Ptr = nullptr; }
|
||||
ComPtr(const ComPtr& other) { Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); }
|
||||
ComPtr(ComPtr&& move) { Ptr = move.Ptr; move.Ptr = nullptr; }
|
||||
~ComPtr() { reset(); }
|
||||
ComPtr& operator=(const ComPtr& other) { if (this != &other) { if (Ptr) Ptr->Release(); Ptr = other.Ptr; if (Ptr) Ptr->AddRef(); } return *this; }
|
||||
void reset() { if (Ptr) Ptr->Release(); Ptr = nullptr; }
|
||||
T* get() { return Ptr; }
|
||||
static IID GetIID() { return __uuidof(T); }
|
||||
void** InitPtr() { return (void**)TypedInitPtr(); }
|
||||
T** TypedInitPtr() { reset(); return &Ptr; }
|
||||
operator T* () const { return Ptr; }
|
||||
T* operator ->() const { return Ptr; }
|
||||
T* Ptr;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,702 +0,0 @@
|
|||
|
||||
#include "win32window.h"
|
||||
#include <windowsx.h>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <dwmapi.h>
|
||||
|
||||
#pragma comment(lib, "dwmapi.lib")
|
||||
|
||||
#ifndef HID_USAGE_PAGE_GENERIC
|
||||
#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
|
||||
#endif
|
||||
|
||||
#ifndef HID_USAGE_GENERIC_MOUSE
|
||||
#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
|
||||
#endif
|
||||
|
||||
#ifndef HID_USAGE_GENERIC_JOYSTICK
|
||||
#define HID_USAGE_GENERIC_JOYSTICK ((USHORT) 0x04)
|
||||
#endif
|
||||
|
||||
#ifndef HID_USAGE_GENERIC_GAMEPAD
|
||||
#define HID_USAGE_GENERIC_GAMEPAD ((USHORT) 0x05)
|
||||
#endif
|
||||
|
||||
#ifndef RIDEV_INPUTSINK
|
||||
#define RIDEV_INPUTSINK (0x100)
|
||||
#endif
|
||||
|
||||
#ifdef MINGW
|
||||
// MinGW's library doesn't contain a thunk for DwmDefWindowProc, so we need to create our own
|
||||
|
||||
BOOL DwmDefWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult )
|
||||
{
|
||||
typedef BOOL(* dwmdwp)(HWND, UINT, WPARAM, LPARAM, LRESULT* );
|
||||
BOOL result(FALSE);
|
||||
HMODULE module = LoadLibrary( _T( "dwmapi.dll" ) );
|
||||
if( module ) {
|
||||
dwmdwp proc = reinterpret_cast<dwmdwp>( GetProcAddress( module, "DwmDefWindowProc" ) );
|
||||
if( proc ) {
|
||||
result = proc( hWnd, msg, wParam, lParam, plResult );
|
||||
}
|
||||
FreeLibrary(module);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static std::string from_utf16(const std::wstring& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
std::string result;
|
||||
result.resize(needed);
|
||||
needed = WideCharToMultiByte(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size(), nullptr, nullptr);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("WideCharToMultiByte failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::wstring to_utf16(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return {};
|
||||
int needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
std::wstring result;
|
||||
result.resize(needed);
|
||||
needed = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &result[0], (int)result.size());
|
||||
if (needed == 0)
|
||||
throw std::runtime_error("MultiByteToWideChar failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
Win32Window::Win32Window(DisplayWindowHost* windowHost) : WindowHost(windowHost)
|
||||
{
|
||||
Windows.push_front(this);
|
||||
WindowsIterator = Windows.begin();
|
||||
|
||||
WNDCLASSEXW classdesc = {};
|
||||
classdesc.cbSize = sizeof(WNDCLASSEX);
|
||||
classdesc.hInstance = GetModuleHandle(0);
|
||||
classdesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
|
||||
classdesc.lpszClassName = L"ZWidgetWindow";
|
||||
classdesc.lpfnWndProc = &Win32Window::WndProc;
|
||||
RegisterClassEx(&classdesc);
|
||||
|
||||
// Microsoft logic at its finest:
|
||||
// WS_EX_DLGMODALFRAME hides the sysmenu icon
|
||||
// WS_CAPTION shows the caption (yay! actually a flag that does what it says it does!)
|
||||
// WS_SYSMENU shows the min/max/close buttons
|
||||
// WS_THICKFRAME makes the window resizable
|
||||
CreateWindowExW(WS_EX_APPWINDOW | WS_EX_DLGMODALFRAME, L"ZWidgetWindow", L"", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 0, 0, 100, 100, 0, 0, GetModuleHandle(0), this);
|
||||
|
||||
/*
|
||||
RAWINPUTDEVICE rid;
|
||||
rid.usUsagePage = HID_USAGE_PAGE_GENERIC;
|
||||
rid.usUsage = HID_USAGE_GENERIC_MOUSE;
|
||||
rid.dwFlags = RIDEV_INPUTSINK;
|
||||
rid.hwndTarget = WindowHandle;
|
||||
BOOL result = RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE));
|
||||
*/
|
||||
}
|
||||
|
||||
Win32Window::~Win32Window()
|
||||
{
|
||||
if (WindowHandle)
|
||||
{
|
||||
DestroyWindow(WindowHandle);
|
||||
WindowHandle = 0;
|
||||
}
|
||||
|
||||
Windows.erase(WindowsIterator);
|
||||
}
|
||||
|
||||
void Win32Window::SetWindowTitle(const std::string& text)
|
||||
{
|
||||
SetWindowText(WindowHandle, to_utf16(text).c_str());
|
||||
}
|
||||
|
||||
void Win32Window::SetBorderColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle, 34/*DWMWA_BORDER_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32Window::SetCaptionColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle, 35/*DWMWA_CAPTION_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32Window::SetCaptionTextColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle, 36/*DWMWA_TEXT_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32Window::SetWindowFrame(const Rect& box)
|
||||
{
|
||||
double dpiscale = GetDpiScale();
|
||||
SetWindowPos(WindowHandle, nullptr, (int)std::round(box.x * dpiscale), (int)std::round(box.y * dpiscale), (int)std::round(box.width * dpiscale), (int)std::round(box.height * dpiscale), SWP_NOACTIVATE | SWP_NOZORDER);
|
||||
}
|
||||
|
||||
void Win32Window::SetClientFrame(const Rect& box)
|
||||
{
|
||||
// This function is currently unused but needs to be disabled because it contains Windows API calls that were only added in Windows 10.
|
||||
#if 0
|
||||
double dpiscale = GetDpiScale();
|
||||
|
||||
RECT rect = {};
|
||||
rect.left = (int)std::round(box.x * dpiscale);
|
||||
rect.top = (int)std::round(box.y * dpiscale);
|
||||
rect.right = rect.left + (int)std::round(box.width * dpiscale);
|
||||
rect.bottom = rect.top + (int)std::round(box.height * dpiscale);
|
||||
|
||||
DWORD style = (DWORD)GetWindowLongPtr(WindowHandle, GWL_STYLE);
|
||||
DWORD exstyle = (DWORD)GetWindowLongPtr(WindowHandle, GWL_EXSTYLE);
|
||||
AdjustWindowRectExForDpi(&rect, style, FALSE, exstyle, GetDpiForWindow(WindowHandle));
|
||||
|
||||
SetWindowPos(WindowHandle, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Win32Window::Show()
|
||||
{
|
||||
ShowWindow(WindowHandle, SW_SHOW);
|
||||
}
|
||||
|
||||
void Win32Window::ShowFullscreen()
|
||||
{
|
||||
HDC screenDC = GetDC(0);
|
||||
int width = GetDeviceCaps(screenDC, HORZRES);
|
||||
int height = GetDeviceCaps(screenDC, VERTRES);
|
||||
ReleaseDC(0, screenDC);
|
||||
SetWindowLongPtr(WindowHandle, GWL_EXSTYLE, WS_EX_APPWINDOW);
|
||||
SetWindowLongPtr(WindowHandle, GWL_STYLE, WS_OVERLAPPED);
|
||||
SetWindowPos(WindowHandle, HWND_TOP, 0, 0, width, height, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
||||
Fullscreen = true;
|
||||
}
|
||||
|
||||
void Win32Window::ShowMaximized()
|
||||
{
|
||||
ShowWindow(WindowHandle, SW_SHOWMAXIMIZED);
|
||||
}
|
||||
|
||||
void Win32Window::ShowMinimized()
|
||||
{
|
||||
ShowWindow(WindowHandle, SW_SHOWMINIMIZED);
|
||||
}
|
||||
|
||||
void Win32Window::ShowNormal()
|
||||
{
|
||||
ShowWindow(WindowHandle, SW_NORMAL);
|
||||
}
|
||||
|
||||
void Win32Window::Hide()
|
||||
{
|
||||
ShowWindow(WindowHandle, SW_HIDE);
|
||||
}
|
||||
|
||||
void Win32Window::Activate()
|
||||
{
|
||||
SetFocus(WindowHandle);
|
||||
}
|
||||
|
||||
void Win32Window::ShowCursor(bool enable)
|
||||
{
|
||||
}
|
||||
|
||||
void Win32Window::LockCursor()
|
||||
{
|
||||
if (!MouseLocked)
|
||||
{
|
||||
MouseLocked = true;
|
||||
GetCursorPos(&MouseLockPos);
|
||||
::ShowCursor(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void Win32Window::UnlockCursor()
|
||||
{
|
||||
if (MouseLocked)
|
||||
{
|
||||
MouseLocked = false;
|
||||
SetCursorPos(MouseLockPos.x, MouseLockPos.y);
|
||||
::ShowCursor(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void Win32Window::CaptureMouse()
|
||||
{
|
||||
SetCapture(WindowHandle);
|
||||
}
|
||||
|
||||
void Win32Window::ReleaseMouseCapture()
|
||||
{
|
||||
ReleaseCapture();
|
||||
}
|
||||
|
||||
void Win32Window::Update()
|
||||
{
|
||||
InvalidateRect(WindowHandle, nullptr, FALSE);
|
||||
}
|
||||
|
||||
bool Win32Window::GetKeyState(EInputKey key)
|
||||
{
|
||||
return ::GetKeyState((int)key) & 0x8000; // High bit (0x8000) means key is down, Low bit (0x0001) means key is sticky on (like Caps Lock, Num Lock, etc.)
|
||||
}
|
||||
|
||||
void Win32Window::SetCursor(StandardCursor cursor)
|
||||
{
|
||||
if (cursor != CurrentCursor)
|
||||
{
|
||||
CurrentCursor = cursor;
|
||||
UpdateCursor();
|
||||
}
|
||||
}
|
||||
|
||||
Rect Win32Window::GetWindowFrame() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetWindowRect(WindowHandle, &box);
|
||||
double dpiscale = GetDpiScale();
|
||||
return Rect(box.left / dpiscale, box.top / dpiscale, box.right / dpiscale, box.bottom / dpiscale);
|
||||
}
|
||||
|
||||
Size Win32Window::GetClientSize() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle, &box);
|
||||
double dpiscale = GetDpiScale();
|
||||
return Size(box.right / dpiscale, box.bottom / dpiscale);
|
||||
}
|
||||
|
||||
int Win32Window::GetPixelWidth() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle, &box);
|
||||
return box.right;
|
||||
}
|
||||
|
||||
int Win32Window::GetPixelHeight() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle, &box);
|
||||
return box.bottom;
|
||||
}
|
||||
|
||||
typedef UINT(WINAPI* GetDpiForWindow_t)(HWND);
|
||||
double Win32Window::GetDpiScale() const
|
||||
{
|
||||
static GetDpiForWindow_t pGetDpiForWindow = nullptr;
|
||||
static bool done = false;
|
||||
if (!done)
|
||||
{
|
||||
HMODULE hMod = GetModuleHandleA("User32.dll");
|
||||
if (hMod != nullptr) pGetDpiForWindow = reinterpret_cast<GetDpiForWindow_t>(GetProcAddress(hMod, "GetDpiForWindow"));
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (pGetDpiForWindow)
|
||||
return pGetDpiForWindow(WindowHandle) / 96.0;
|
||||
else
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
std::string Win32Window::GetClipboardText()
|
||||
{
|
||||
BOOL result = OpenClipboard(WindowHandle);
|
||||
if (result == FALSE)
|
||||
throw std::runtime_error("Unable to open clipboard");
|
||||
|
||||
HANDLE handle = GetClipboardData(CF_UNICODETEXT);
|
||||
if (handle == 0)
|
||||
{
|
||||
CloseClipboard();
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::wstring::value_type* data = (std::wstring::value_type*)GlobalLock(handle);
|
||||
if (data == 0)
|
||||
{
|
||||
CloseClipboard();
|
||||
return std::string();
|
||||
}
|
||||
std::string str = from_utf16(data);
|
||||
GlobalUnlock(handle);
|
||||
|
||||
CloseClipboard();
|
||||
return str;
|
||||
}
|
||||
|
||||
void Win32Window::SetClipboardText(const std::string& text)
|
||||
{
|
||||
std::wstring text16 = to_utf16(text);
|
||||
|
||||
BOOL result = OpenClipboard(WindowHandle);
|
||||
if (result == FALSE)
|
||||
throw std::runtime_error("Unable to open clipboard");
|
||||
|
||||
result = EmptyClipboard();
|
||||
if (result == FALSE)
|
||||
{
|
||||
CloseClipboard();
|
||||
throw std::runtime_error("Unable to empty clipboard");
|
||||
}
|
||||
|
||||
unsigned int length = (text16.length() + 1) * sizeof(std::wstring::value_type);
|
||||
HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, length);
|
||||
if (handle == 0)
|
||||
{
|
||||
CloseClipboard();
|
||||
throw std::runtime_error("Unable to allocate clipboard memory");
|
||||
}
|
||||
|
||||
void* data = GlobalLock(handle);
|
||||
if (data == 0)
|
||||
{
|
||||
GlobalFree(handle);
|
||||
CloseClipboard();
|
||||
throw std::runtime_error("Unable to lock clipboard memory");
|
||||
}
|
||||
memcpy(data, text16.c_str(), length);
|
||||
GlobalUnlock(handle);
|
||||
|
||||
HANDLE data_result = SetClipboardData(CF_UNICODETEXT, handle);
|
||||
|
||||
if (data_result == 0)
|
||||
{
|
||||
GlobalFree(handle);
|
||||
CloseClipboard();
|
||||
throw std::runtime_error("Unable to set clipboard data");
|
||||
}
|
||||
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
void Win32Window::PresentBitmap(int width, int height, const uint32_t* pixels)
|
||||
{
|
||||
BITMAPV5HEADER header = {};
|
||||
header.bV5Size = sizeof(BITMAPV5HEADER);
|
||||
header.bV5Width = width;
|
||||
header.bV5Height = -height;
|
||||
header.bV5Planes = 1;
|
||||
header.bV5BitCount = 32;
|
||||
header.bV5Compression = BI_BITFIELDS;
|
||||
header.bV5AlphaMask = 0xff000000;
|
||||
header.bV5RedMask = 0x00ff0000;
|
||||
header.bV5GreenMask = 0x0000ff00;
|
||||
header.bV5BlueMask = 0x000000ff;
|
||||
header.bV5SizeImage = width * height * sizeof(uint32_t);
|
||||
header.bV5CSType = LCS_sRGB;
|
||||
|
||||
HDC dc = PaintDC;
|
||||
if (dc != 0)
|
||||
{
|
||||
int result = SetDIBitsToDevice(dc, 0, 0, width, height, 0, 0, 0, height, pixels, (const BITMAPINFO*)&header, BI_RGB);
|
||||
ReleaseDC(WindowHandle, dc);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT Win32Window::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
LPARAM result = 0;
|
||||
|
||||
if (DwmDefWindowProc(WindowHandle, msg, wparam, lparam, &result))
|
||||
return result;
|
||||
|
||||
if (msg == WM_INPUT)
|
||||
{
|
||||
bool hasFocus = GetFocus() != 0;
|
||||
|
||||
HRAWINPUT handle = (HRAWINPUT)lparam;
|
||||
UINT size = 0;
|
||||
UINT result = GetRawInputData(handle, RID_INPUT, 0, &size, sizeof(RAWINPUTHEADER));
|
||||
if (result == 0 && size > 0)
|
||||
{
|
||||
size *= 2;
|
||||
std::vector<uint8_t*> buffer(size);
|
||||
result = GetRawInputData(handle, RID_INPUT, buffer.data(), &size, sizeof(RAWINPUTHEADER));
|
||||
if (result >= 0)
|
||||
{
|
||||
RAWINPUT* rawinput = (RAWINPUT*)buffer.data();
|
||||
if (rawinput->header.dwType == RIM_TYPEMOUSE)
|
||||
{
|
||||
if (hasFocus)
|
||||
WindowHost->OnWindowRawMouseMove(rawinput->data.mouse.lLastX, rawinput->data.mouse.lLastY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return DefWindowProc(WindowHandle, msg, wparam, lparam);
|
||||
}
|
||||
else if (msg == WM_PAINT)
|
||||
{
|
||||
PAINTSTRUCT paintStruct = {};
|
||||
PaintDC = BeginPaint(WindowHandle, &paintStruct);
|
||||
if (PaintDC)
|
||||
{
|
||||
WindowHost->OnWindowPaint();
|
||||
EndPaint(WindowHandle, &paintStruct);
|
||||
PaintDC = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (msg == WM_ACTIVATE)
|
||||
{
|
||||
WindowHost->OnWindowActivated();
|
||||
}
|
||||
else if (msg == WM_MOUSEMOVE)
|
||||
{
|
||||
if (MouseLocked && GetFocus() != 0)
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle, &box);
|
||||
|
||||
POINT center = {};
|
||||
center.x = box.right / 2;
|
||||
center.y = box.bottom / 2;
|
||||
ClientToScreen(WindowHandle, ¢er);
|
||||
|
||||
SetCursorPos(center.x, center.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCursor();
|
||||
}
|
||||
|
||||
WindowHost->OnWindowMouseMove(GetLParamPos(lparam));
|
||||
}
|
||||
else if (msg == WM_LBUTTONDOWN)
|
||||
{
|
||||
WindowHost->OnWindowMouseDown(GetLParamPos(lparam), IK_LeftMouse);
|
||||
}
|
||||
else if (msg == WM_LBUTTONDBLCLK)
|
||||
{
|
||||
WindowHost->OnWindowMouseDoubleclick(GetLParamPos(lparam), IK_LeftMouse);
|
||||
}
|
||||
else if (msg == WM_LBUTTONUP)
|
||||
{
|
||||
WindowHost->OnWindowMouseUp(GetLParamPos(lparam), IK_LeftMouse);
|
||||
}
|
||||
else if (msg == WM_MBUTTONDOWN)
|
||||
{
|
||||
WindowHost->OnWindowMouseDown(GetLParamPos(lparam), IK_MiddleMouse);
|
||||
}
|
||||
else if (msg == WM_MBUTTONDBLCLK)
|
||||
{
|
||||
WindowHost->OnWindowMouseDoubleclick(GetLParamPos(lparam), IK_MiddleMouse);
|
||||
}
|
||||
else if (msg == WM_MBUTTONUP)
|
||||
{
|
||||
WindowHost->OnWindowMouseUp(GetLParamPos(lparam), IK_MiddleMouse);
|
||||
}
|
||||
else if (msg == WM_RBUTTONDOWN)
|
||||
{
|
||||
WindowHost->OnWindowMouseDown(GetLParamPos(lparam), IK_RightMouse);
|
||||
}
|
||||
else if (msg == WM_RBUTTONDBLCLK)
|
||||
{
|
||||
WindowHost->OnWindowMouseDoubleclick(GetLParamPos(lparam), IK_RightMouse);
|
||||
}
|
||||
else if (msg == WM_RBUTTONUP)
|
||||
{
|
||||
WindowHost->OnWindowMouseUp(GetLParamPos(lparam), IK_RightMouse);
|
||||
}
|
||||
else if (msg == WM_MOUSEWHEEL)
|
||||
{
|
||||
double delta = GET_WHEEL_DELTA_WPARAM(wparam) / (double)WHEEL_DELTA;
|
||||
|
||||
// Note: WM_MOUSEWHEEL uses screen coordinates. GetLParamPos assumes client coordinates.
|
||||
double dpiscale = GetDpiScale();
|
||||
POINT pos;
|
||||
pos.x = GET_X_LPARAM(lparam);
|
||||
pos.y = GET_Y_LPARAM(lparam);
|
||||
ScreenToClient(WindowHandle, &pos);
|
||||
|
||||
WindowHost->OnWindowMouseWheel(Point(pos.x / dpiscale, pos.y / dpiscale), delta < 0.0 ? IK_MouseWheelDown : IK_MouseWheelUp);
|
||||
}
|
||||
else if (msg == WM_CHAR)
|
||||
{
|
||||
wchar_t buf[2] = { (wchar_t)wparam, 0 };
|
||||
WindowHost->OnWindowKeyChar(from_utf16(buf));
|
||||
}
|
||||
else if (msg == WM_KEYDOWN)
|
||||
{
|
||||
WindowHost->OnWindowKeyDown((EInputKey)wparam);
|
||||
}
|
||||
else if (msg == WM_KEYUP)
|
||||
{
|
||||
WindowHost->OnWindowKeyUp((EInputKey)wparam);
|
||||
}
|
||||
else if (msg == WM_SETFOCUS)
|
||||
{
|
||||
if (MouseLocked)
|
||||
{
|
||||
::ShowCursor(FALSE);
|
||||
}
|
||||
}
|
||||
else if (msg == WM_KILLFOCUS)
|
||||
{
|
||||
if (MouseLocked)
|
||||
{
|
||||
::ShowCursor(TRUE);
|
||||
}
|
||||
}
|
||||
else if (msg == WM_CLOSE)
|
||||
{
|
||||
WindowHost->OnWindowClose();
|
||||
return 0;
|
||||
}
|
||||
else if (msg == WM_SIZE)
|
||||
{
|
||||
WindowHost->OnWindowGeometryChanged();
|
||||
return 0;
|
||||
}
|
||||
/*else if (msg == WM_NCCALCSIZE && wparam == TRUE) // calculate client area for the window
|
||||
{
|
||||
NCCALCSIZE_PARAMS* calcsize = (NCCALCSIZE_PARAMS*)lparam;
|
||||
return WVR_REDRAW;
|
||||
}*/
|
||||
|
||||
return DefWindowProc(WindowHandle, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
void Win32Window::UpdateCursor()
|
||||
{
|
||||
LPCWSTR cursor = IDC_ARROW;
|
||||
switch (CurrentCursor)
|
||||
{
|
||||
case StandardCursor::arrow: cursor = IDC_ARROW; break;
|
||||
case StandardCursor::appstarting: cursor = IDC_APPSTARTING; break;
|
||||
case StandardCursor::cross: cursor = IDC_CROSS; break;
|
||||
case StandardCursor::hand: cursor = IDC_HAND; break;
|
||||
case StandardCursor::ibeam: cursor = IDC_IBEAM; break;
|
||||
case StandardCursor::no: cursor = IDC_NO; break;
|
||||
case StandardCursor::size_all: cursor = IDC_SIZEALL; break;
|
||||
case StandardCursor::size_nesw: cursor = IDC_SIZENESW; break;
|
||||
case StandardCursor::size_ns: cursor = IDC_SIZENS; break;
|
||||
case StandardCursor::size_nwse: cursor = IDC_SIZENWSE; break;
|
||||
case StandardCursor::size_we: cursor = IDC_SIZEWE; break;
|
||||
case StandardCursor::uparrow: cursor = IDC_UPARROW; break;
|
||||
case StandardCursor::wait: cursor = IDC_WAIT; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
::SetCursor((HCURSOR)LoadImage(0, cursor, IMAGE_CURSOR, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_SHARED));
|
||||
}
|
||||
|
||||
Point Win32Window::GetLParamPos(LPARAM lparam) const
|
||||
{
|
||||
double dpiscale = GetDpiScale();
|
||||
return Point(GET_X_LPARAM(lparam) / dpiscale, GET_Y_LPARAM(lparam) / dpiscale);
|
||||
}
|
||||
|
||||
LRESULT Win32Window::WndProc(HWND windowhandle, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
if (msg == WM_CREATE)
|
||||
{
|
||||
CREATESTRUCT* createstruct = (CREATESTRUCT*)lparam;
|
||||
Win32Window* viewport = (Win32Window*)createstruct->lpCreateParams;
|
||||
viewport->WindowHandle = windowhandle;
|
||||
SetWindowLongPtr(windowhandle, GWLP_USERDATA, (LONG_PTR)viewport);
|
||||
return viewport->OnWindowMessage(msg, wparam, lparam);
|
||||
}
|
||||
else
|
||||
{
|
||||
Win32Window* viewport = (Win32Window*)GetWindowLongPtr(windowhandle, GWLP_USERDATA);
|
||||
if (viewport)
|
||||
{
|
||||
LRESULT result = viewport->OnWindowMessage(msg, wparam, lparam);
|
||||
if (msg == WM_DESTROY)
|
||||
{
|
||||
SetWindowLongPtr(windowhandle, GWLP_USERDATA, 0);
|
||||
viewport->WindowHandle = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DefWindowProc(windowhandle, msg, wparam, lparam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Win32Window::ProcessEvents()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
MSG msg = {};
|
||||
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) <= 0)
|
||||
break;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
void Win32Window::RunLoop()
|
||||
{
|
||||
while (!ExitRunLoop && !Windows.empty())
|
||||
{
|
||||
MSG msg = {};
|
||||
if (GetMessage(&msg, 0, 0, 0) <= 0)
|
||||
break;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
ExitRunLoop = false;
|
||||
}
|
||||
|
||||
void Win32Window::ExitLoop()
|
||||
{
|
||||
ExitRunLoop = true;
|
||||
}
|
||||
|
||||
Size Win32Window::GetScreenSize()
|
||||
{
|
||||
HDC screenDC = GetDC(0);
|
||||
int screenWidth = GetDeviceCaps(screenDC, HORZRES);
|
||||
int screenHeight = GetDeviceCaps(screenDC, VERTRES);
|
||||
double dpiScale = GetDeviceCaps(screenDC, LOGPIXELSX) / 96.0;
|
||||
ReleaseDC(0, screenDC);
|
||||
|
||||
return Size(screenWidth / dpiScale, screenHeight / dpiScale);
|
||||
}
|
||||
|
||||
static void CALLBACK Win32TimerCallback(HWND handle, UINT message, UINT_PTR timerID, DWORD timestamp)
|
||||
{
|
||||
auto it = Win32Window::Timers.find(timerID);
|
||||
if (it != Win32Window::Timers.end())
|
||||
{
|
||||
it->second();
|
||||
}
|
||||
}
|
||||
|
||||
void* Win32Window::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
UINT_PTR result = SetTimer(0, 0, timeoutMilliseconds, Win32TimerCallback);
|
||||
if (result == 0)
|
||||
throw std::runtime_error("Could not create timer");
|
||||
Timers[result] = std::move(onTimer);
|
||||
return (void*)result;
|
||||
}
|
||||
|
||||
void Win32Window::StopTimer(void* timerID)
|
||||
{
|
||||
auto it = Timers.find((UINT_PTR)timerID);
|
||||
if (it != Timers.end())
|
||||
{
|
||||
Timers.erase(it);
|
||||
KillTimer(0, (UINT_PTR)timerID);
|
||||
}
|
||||
}
|
||||
|
||||
std::list<Win32Window*> Win32Window::Windows;
|
||||
bool Win32Window::ExitRunLoop;
|
||||
|
||||
std::unordered_map<UINT_PTR, std::function<void()>> Win32Window::Timers;
|
||||
|
|
@ -1,120 +1,201 @@
|
|||
|
||||
#include "window/window.h"
|
||||
#include "window/stub/stub_open_folder_dialog.h"
|
||||
#include "window/stub/stub_open_file_dialog.h"
|
||||
#include "window/stub/stub_save_file_dialog.h"
|
||||
#include "window/sdl2nativehandle.h"
|
||||
#include "core/widget.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "win32/win32window.h"
|
||||
|
||||
std::unique_ptr<DisplayWindow> DisplayWindow::Create(DisplayWindowHost* windowHost)
|
||||
std::unique_ptr<DisplayWindow> DisplayWindow::Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI)
|
||||
{
|
||||
return std::make_unique<Win32Window>(windowHost);
|
||||
return DisplayBackend::Get()->Create(windowHost, popupWindow, owner, renderAPI);
|
||||
}
|
||||
|
||||
void DisplayWindow::ProcessEvents()
|
||||
{
|
||||
Win32Window::ProcessEvents();
|
||||
DisplayBackend::Get()->ProcessEvents();
|
||||
}
|
||||
|
||||
void DisplayWindow::RunLoop()
|
||||
{
|
||||
Win32Window::RunLoop();
|
||||
DisplayBackend::Get()->RunLoop();
|
||||
}
|
||||
|
||||
void DisplayWindow::ExitLoop()
|
||||
{
|
||||
Win32Window::ExitLoop();
|
||||
}
|
||||
|
||||
Size DisplayWindow::GetScreenSize()
|
||||
{
|
||||
return Win32Window::GetScreenSize();
|
||||
DisplayBackend::Get()->ExitLoop();
|
||||
}
|
||||
|
||||
void* DisplayWindow::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
return Win32Window::StartTimer(timeoutMilliseconds, std::move(onTimer));
|
||||
return DisplayBackend::Get()->StartTimer(timeoutMilliseconds, onTimer);
|
||||
}
|
||||
|
||||
void DisplayWindow::StopTimer(void* timerID)
|
||||
{
|
||||
Win32Window::StopTimer(timerID);
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
std::unique_ptr<DisplayWindow> DisplayWindow::Create(DisplayWindowHost* windowHost)
|
||||
{
|
||||
throw std::runtime_error("DisplayWindow::Create not implemented");
|
||||
}
|
||||
|
||||
void DisplayWindow::ProcessEvents()
|
||||
{
|
||||
throw std::runtime_error("DisplayWindow::ProcessEvents not implemented");
|
||||
}
|
||||
|
||||
void DisplayWindow::RunLoop()
|
||||
{
|
||||
throw std::runtime_error("DisplayWindow::RunLoop not implemented");
|
||||
}
|
||||
|
||||
void DisplayWindow::ExitLoop()
|
||||
{
|
||||
throw std::runtime_error("DisplayWindow::ExitLoop not implemented");
|
||||
DisplayBackend::Get()->StopTimer(timerID);
|
||||
}
|
||||
|
||||
Size DisplayWindow::GetScreenSize()
|
||||
{
|
||||
throw std::runtime_error("DisplayWindow::GetScreenSize not implemented");
|
||||
return DisplayBackend::Get()->GetScreenSize();
|
||||
}
|
||||
|
||||
void* DisplayWindow::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::unique_ptr<DisplayBackend>& GetBackendVar()
|
||||
{
|
||||
throw std::runtime_error("DisplayWindow::StartTimer not implemented");
|
||||
// In C++, static variables in functions are constructed on first encounter and is destructed in the reverse order when main() ends.
|
||||
static std::unique_ptr<DisplayBackend> p;
|
||||
return p;
|
||||
}
|
||||
|
||||
void DisplayWindow::StopTimer(void* timerID)
|
||||
DisplayBackend* DisplayBackend::Get()
|
||||
{
|
||||
throw std::runtime_error("DisplayWindow::StopTimer not implemented");
|
||||
return GetBackendVar().get();
|
||||
}
|
||||
|
||||
void DisplayBackend::Set(std::unique_ptr<DisplayBackend> instance)
|
||||
{
|
||||
GetBackendVar() = std::move(instance);
|
||||
}
|
||||
|
||||
std::unique_ptr<OpenFileDialog> DisplayBackend::CreateOpenFileDialog(DisplayWindow* owner)
|
||||
{
|
||||
return std::make_unique<StubOpenFileDialog>(owner);
|
||||
}
|
||||
|
||||
std::unique_ptr<SaveFileDialog> DisplayBackend::CreateSaveFileDialog(DisplayWindow* owner)
|
||||
{
|
||||
return std::make_unique<StubSaveFileDialog>(owner);
|
||||
}
|
||||
|
||||
std::unique_ptr<OpenFolderDialog> DisplayBackend::CreateOpenFolderDialog(DisplayWindow* owner)
|
||||
{
|
||||
return std::make_unique<StubOpenFolderDialog>(owner);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4996) // warning C4996 : 'getenv' : This function or variable may be unsafe.Consider using _dupenv_s instead.To disable deprecation, use _CRT_SECURE_NO_WARNINGS.See online help for details.
|
||||
#endif
|
||||
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateBackend()
|
||||
{
|
||||
std::unique_ptr<DisplayBackend> backend;
|
||||
|
||||
// Check if there is an environment variable specified for the desired backend
|
||||
const char* backendSelectionEnv = std::getenv("ZWIDGET_DISPLAY_BACKEND");
|
||||
if (backendSelectionEnv)
|
||||
{
|
||||
std::string backendSelectionStr(backendSelectionEnv);
|
||||
if (backendSelectionStr == "Win32")
|
||||
{
|
||||
backend = TryCreateWin32();
|
||||
}
|
||||
else if (backendSelectionStr == "X11")
|
||||
{
|
||||
backend = TryCreateX11();
|
||||
}
|
||||
else if (backendSelectionStr == "SDL2")
|
||||
{
|
||||
backend = TryCreateSDL2();
|
||||
}
|
||||
}
|
||||
|
||||
if (!backend)
|
||||
{
|
||||
backend = TryCreateWin32();
|
||||
if (!backend) backend = TryCreateWayland();
|
||||
if (!backend) backend = TryCreateX11();
|
||||
if (!backend) backend = TryCreateSDL2();
|
||||
}
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "win32/win32_display_backend.h"
|
||||
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateWin32()
|
||||
{
|
||||
return std::make_unique<Win32DisplayBackend>();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "sdl2/sdl2displaywindow.h"
|
||||
|
||||
std::unique_ptr<DisplayWindow> DisplayWindow::Create(DisplayWindowHost* windowHost)
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateWin32()
|
||||
{
|
||||
return std::make_unique<SDL2DisplayWindow>(windowHost);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DisplayWindow::ProcessEvents()
|
||||
#endif
|
||||
|
||||
#ifdef USE_SDL2
|
||||
|
||||
#include "sdl2/sdl2_display_backend.h"
|
||||
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateSDL2()
|
||||
{
|
||||
SDL2DisplayWindow::ProcessEvents();
|
||||
return std::make_unique<SDL2DisplayBackend>();
|
||||
}
|
||||
|
||||
void DisplayWindow::RunLoop()
|
||||
#else
|
||||
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateSDL2()
|
||||
{
|
||||
SDL2DisplayWindow::RunLoop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_X11
|
||||
|
||||
void DisplayWindow::ExitLoop()
|
||||
#include "x11/x11_display_backend.h"
|
||||
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateX11()
|
||||
{
|
||||
SDL2DisplayWindow::ExitLoop();
|
||||
try
|
||||
{
|
||||
return std::make_unique<X11DisplayBackend>();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Size DisplayWindow::GetScreenSize()
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateX11()
|
||||
{
|
||||
return SDL2DisplayWindow::GetScreenSize();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void* DisplayWindow::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
#ifdef USE_WAYLAND
|
||||
|
||||
#include "wayland/wayland_display_backend.h"
|
||||
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateWayland()
|
||||
{
|
||||
return SDL2DisplayWindow::StartTimer(timeoutMilliseconds, std::move(onTimer));
|
||||
try
|
||||
{
|
||||
return std::make_unique<WaylandDisplayBackend>();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void DisplayWindow::StopTimer(void* timerID)
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateWayland()
|
||||
{
|
||||
SDL2DisplayWindow::StopTimer(timerID);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
70
libraries/ZWidget/src/window/x11/x11_display_backend.cpp
Normal file
70
libraries/ZWidget/src/window/x11/x11_display_backend.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
|
||||
#include "x11_display_backend.h"
|
||||
#include "x11_display_window.h"
|
||||
|
||||
#ifdef USE_DBUS
|
||||
#include "window/dbus/dbus_open_file_dialog.h"
|
||||
#include "window/dbus/dbus_save_file_dialog.h"
|
||||
#include "window/dbus/dbus_open_folder_dialog.h"
|
||||
#endif
|
||||
|
||||
std::unique_ptr<DisplayWindow> X11DisplayBackend::Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI)
|
||||
{
|
||||
return std::make_unique<X11DisplayWindow>(windowHost, popupWindow, static_cast<X11DisplayWindow*>(owner), renderAPI);
|
||||
}
|
||||
|
||||
void X11DisplayBackend::ProcessEvents()
|
||||
{
|
||||
X11DisplayWindow::ProcessEvents();
|
||||
}
|
||||
|
||||
void X11DisplayBackend::RunLoop()
|
||||
{
|
||||
X11DisplayWindow::RunLoop();
|
||||
}
|
||||
|
||||
void X11DisplayBackend::ExitLoop()
|
||||
{
|
||||
X11DisplayWindow::ExitLoop();
|
||||
}
|
||||
|
||||
Size X11DisplayBackend::GetScreenSize()
|
||||
{
|
||||
return X11DisplayWindow::GetScreenSize();
|
||||
}
|
||||
|
||||
void* X11DisplayBackend::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
return X11DisplayWindow::StartTimer(timeoutMilliseconds, std::move(onTimer));
|
||||
}
|
||||
|
||||
void X11DisplayBackend::StopTimer(void* timerID)
|
||||
{
|
||||
X11DisplayWindow::StopTimer(timerID);
|
||||
}
|
||||
|
||||
#ifdef USE_DBUS
|
||||
std::unique_ptr<OpenFileDialog> X11DisplayBackend::CreateOpenFileDialog(DisplayWindow* owner)
|
||||
{
|
||||
std::string ownerHandle;
|
||||
if (owner)
|
||||
ownerHandle = "x11:" + std::to_string(static_cast<X11DisplayWindow*>(owner)->window);
|
||||
return std::make_unique<DBusOpenFileDialog>(ownerHandle);
|
||||
}
|
||||
|
||||
std::unique_ptr<SaveFileDialog> X11DisplayBackend::CreateSaveFileDialog(DisplayWindow* owner)
|
||||
{
|
||||
std::string ownerHandle;
|
||||
if (owner)
|
||||
ownerHandle = "x11:" + std::to_string(static_cast<X11DisplayWindow*>(owner)->window);
|
||||
return std::make_unique<DBusSaveFileDialog>(ownerHandle);
|
||||
}
|
||||
|
||||
std::unique_ptr<OpenFolderDialog> X11DisplayBackend::CreateOpenFolderDialog(DisplayWindow* owner)
|
||||
{
|
||||
std::string ownerHandle;
|
||||
if (owner)
|
||||
ownerHandle = "x11:" + std::to_string(static_cast<X11DisplayWindow*>(owner)->window);
|
||||
return std::make_unique<DBusOpenFolderDialog>(ownerHandle);
|
||||
}
|
||||
#endif
|
||||
25
libraries/ZWidget/src/window/x11/x11_display_backend.h
Normal file
25
libraries/ZWidget/src/window/x11/x11_display_backend.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "window/window.h"
|
||||
|
||||
class X11DisplayBackend : public DisplayBackend
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI) override;
|
||||
void ProcessEvents() override;
|
||||
void RunLoop() override;
|
||||
void ExitLoop() override;
|
||||
|
||||
void* StartTimer(int timeoutMilliseconds, std::function<void()> onTimer) override;
|
||||
void StopTimer(void* timerID) override;
|
||||
|
||||
Size GetScreenSize() override;
|
||||
|
||||
bool IsX11() override { return true; }
|
||||
|
||||
#ifdef USE_DBUS
|
||||
std::unique_ptr<OpenFileDialog> CreateOpenFileDialog(DisplayWindow* owner) override;
|
||||
std::unique_ptr<SaveFileDialog> CreateSaveFileDialog(DisplayWindow* owner) override;
|
||||
std::unique_ptr<OpenFolderDialog> CreateOpenFolderDialog(DisplayWindow* owner) override;
|
||||
#endif
|
||||
};
|
||||
1184
libraries/ZWidget/src/window/x11/x11_display_window.cpp
Normal file
1184
libraries/ZWidget/src/window/x11/x11_display_window.cpp
Normal file
File diff suppressed because it is too large
Load diff
142
libraries/ZWidget/src/window/x11/x11_display_window.h
Normal file
142
libraries/ZWidget/src/window/x11/x11_display_window.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#pragma once
|
||||
|
||||
#include <zwidget/window/window.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/keysymdef.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <map>
|
||||
|
||||
class X11DisplayWindow : public DisplayWindow
|
||||
{
|
||||
public:
|
||||
X11DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, X11DisplayWindow* owner, RenderAPI renderAPI);
|
||||
~X11DisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) override;
|
||||
void SetWindowFrame(const Rect& box) override;
|
||||
void SetClientFrame(const Rect& box) override;
|
||||
void Show() override;
|
||||
void ShowFullscreen() override;
|
||||
void ShowMaximized() override;
|
||||
void ShowMinimized() override;
|
||||
void ShowNormal() override;
|
||||
bool IsWindowFullscreen() override;
|
||||
void Hide() override;
|
||||
void Activate() override;
|
||||
void ShowCursor(bool enable) override;
|
||||
void LockCursor() override;
|
||||
void UnlockCursor() override;
|
||||
void CaptureMouse() override;
|
||||
void ReleaseMouseCapture() override;
|
||||
void Update() override;
|
||||
bool GetKeyState(InputKey key) override;
|
||||
|
||||
void SetCursor(StandardCursor cursor) override;
|
||||
|
||||
Rect GetWindowFrame() const override;
|
||||
Size GetClientSize() const override;
|
||||
int GetPixelWidth() const override;
|
||||
int GetPixelHeight() const override;
|
||||
double GetDpiScale() const override;
|
||||
|
||||
void PresentBitmap(int width, int height, const uint32_t* pixels) override;
|
||||
|
||||
void SetBorderColor(uint32_t bgra8) override;
|
||||
void SetCaptionColor(uint32_t bgra8) override;
|
||||
void SetCaptionTextColor(uint32_t bgra8) override;
|
||||
|
||||
std::string GetClipboardText() override;
|
||||
void SetClipboardText(const std::string& text) override;
|
||||
|
||||
Point MapFromGlobal(const Point& pos) const override;
|
||||
Point MapToGlobal(const Point& pos) const override;
|
||||
|
||||
void* GetNativeHandle() override;
|
||||
|
||||
std::vector<std::string> GetVulkanInstanceExtensions() override;
|
||||
VkSurfaceKHR CreateVulkanSurface(VkInstance instance) override;
|
||||
|
||||
static void ProcessEvents();
|
||||
static void RunLoop();
|
||||
static void ExitLoop();
|
||||
static Size GetScreenSize();
|
||||
static void* StartTimer(int timeoutMilliseconds, std::function<void()> onTimer);
|
||||
static void StopTimer(void* timerID);
|
||||
|
||||
private:
|
||||
void UpdateCursor();
|
||||
|
||||
void OnEvent(XEvent* event);
|
||||
void OnConfigureNotify(XEvent* event);
|
||||
void OnClientMessage(XEvent* event);
|
||||
void OnExpose(XEvent* event);
|
||||
void OnFocusIn(XEvent* event);
|
||||
void OnFocusOut(XEvent* event);
|
||||
void OnPropertyNotify(XEvent* event);
|
||||
void OnKeyPress(XEvent* event);
|
||||
void OnKeyRelease(XEvent* event);
|
||||
void OnButtonPress(XEvent* event);
|
||||
void OnButtonRelease(XEvent* event);
|
||||
void OnMotionNotify(XEvent* event);
|
||||
void OnLeaveNotify(XEvent* event);
|
||||
void OnSelectionClear(XEvent* event);
|
||||
void OnSelectionNotify(XEvent* event);
|
||||
void OnSelectionRequest(XEvent* event);
|
||||
|
||||
void CreateBackbuffer(int width, int height);
|
||||
void DestroyBackbuffer();
|
||||
|
||||
InputKey GetInputKey(XEvent* event);
|
||||
Point GetMousePos(XEvent* event);
|
||||
|
||||
static bool WaitForEvents(int timeout);
|
||||
static void DispatchEvent(XEvent* event);
|
||||
|
||||
static void CheckNeedsUpdate();
|
||||
|
||||
std::vector<uint8_t> GetWindowProperty(Atom property, Atom &actual_type, int &actual_format, unsigned long &item_count);
|
||||
|
||||
DisplayWindowHost* windowHost = nullptr;
|
||||
X11DisplayWindow* owner = nullptr;
|
||||
Display* display = nullptr;
|
||||
Window window = {};
|
||||
int screen = 0;
|
||||
int depth = 0;
|
||||
Visual* visual = nullptr;
|
||||
Colormap colormap = {};
|
||||
XIC xic = nullptr;
|
||||
StandardCursor cursor = {};
|
||||
bool isCursorEnabled = true;
|
||||
bool isMapped = false;
|
||||
bool isMinimized = false;
|
||||
bool isFullscreen = false;
|
||||
double dpiScale = 1.0;
|
||||
|
||||
int ClientSizeX = 0;
|
||||
int ClientSizeY = 0;
|
||||
int MouseX = -1;
|
||||
int MouseY = -1;
|
||||
|
||||
Pixmap cursor_bitmap = None;
|
||||
Cursor hidden_cursor = None;
|
||||
|
||||
std::map<InputKey, bool> keyState;
|
||||
|
||||
std::string clipboardText;
|
||||
|
||||
struct
|
||||
{
|
||||
Pixmap pixmap = None;
|
||||
XImage* image = nullptr;
|
||||
void* pixels = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
} backbuffer;
|
||||
|
||||
bool needsUpdate = false;
|
||||
|
||||
friend class X11DisplayBackend;
|
||||
};
|
||||
73
libraries/ZWidget/src/window/ztimer/ztimer.cpp
Normal file
73
libraries/ZWidget/src/window/ztimer/ztimer.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#include "ztimer.h"
|
||||
|
||||
ZTimer::ZTimer()
|
||||
{
|
||||
}
|
||||
|
||||
ZTimer::ZTimer(Duration duration_ms) : m_timerDuration(duration_ms)
|
||||
{
|
||||
}
|
||||
|
||||
ZTimer::ZTimer(Duration duration_ms, CallbackFunc callback)
|
||||
: m_timerDuration(duration_ms), m_callback(callback)
|
||||
{
|
||||
}
|
||||
|
||||
ZTimer::~ZTimer()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void ZTimer::Start()
|
||||
{
|
||||
m_startTime = Clock::now();
|
||||
m_currentTime = m_startTime;
|
||||
m_timerStarted = true;
|
||||
m_timerFinished = false;
|
||||
}
|
||||
|
||||
void ZTimer::Stop()
|
||||
{
|
||||
m_timerStarted = false;
|
||||
}
|
||||
|
||||
void ZTimer::SetDuration(Duration duration_ms)
|
||||
{
|
||||
if (m_timerStarted)
|
||||
return;
|
||||
m_timerDuration = duration_ms;
|
||||
}
|
||||
|
||||
void ZTimer::SetCallback(std::function<void()> callback)
|
||||
{
|
||||
if (m_timerStarted)
|
||||
return;
|
||||
m_callback = callback;
|
||||
}
|
||||
|
||||
void ZTimer::SetRepeating(bool value)
|
||||
{
|
||||
if (m_timerStarted)
|
||||
return;
|
||||
m_repeatingTimer = value;
|
||||
}
|
||||
|
||||
void ZTimer::Update(Duration deltaTime)
|
||||
{
|
||||
if (!m_timerStarted)
|
||||
return;
|
||||
|
||||
m_currentTime += deltaTime;
|
||||
|
||||
if (m_currentTime >= m_startTime + m_timerDuration)
|
||||
{
|
||||
m_callback();
|
||||
if (!m_repeatingTimer)
|
||||
{
|
||||
Stop();
|
||||
m_timerFinished = true;
|
||||
}
|
||||
else
|
||||
Start();
|
||||
}
|
||||
}
|
||||
41
libraries/ZWidget/src/window/ztimer/ztimer.h
Normal file
41
libraries/ZWidget/src/window/ztimer/ztimer.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
// ZTimer: A small, independent timer
|
||||
// Useful for implementing timed events on your backends
|
||||
class ZTimer
|
||||
{
|
||||
public:
|
||||
using Duration = std::chrono::duration<double, std::milli>;
|
||||
using Clock = std::chrono::system_clock;
|
||||
using TimePoint = std::chrono::time_point<Clock, Duration>;
|
||||
using CallbackFunc = std::function<void()>;
|
||||
|
||||
ZTimer();
|
||||
ZTimer(Duration duration_ms);
|
||||
ZTimer(Duration duration_ms, CallbackFunc callback);
|
||||
|
||||
~ZTimer();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void SetDuration(Duration duration_ms);
|
||||
void SetCallback(CallbackFunc callback);
|
||||
void SetRepeating(bool value);
|
||||
void Update(Duration deltaTime);
|
||||
|
||||
bool IsStarted() { return m_timerStarted; }
|
||||
bool IsFinished() { return m_timerFinished; }
|
||||
|
||||
private:
|
||||
bool m_timerStarted = false;
|
||||
bool m_repeatingTimer = false;
|
||||
bool m_timerFinished = false;
|
||||
|
||||
TimePoint m_startTime;
|
||||
TimePoint m_currentTime;
|
||||
Duration m_timerDuration;
|
||||
CallbackFunc m_callback;
|
||||
};
|
||||
|
|
@ -6,14 +6,16 @@
|
|||
#include <zwidget/core/image.h>
|
||||
#include <zwidget/widgets/pushbutton/pushbutton.h>
|
||||
#include <zwidget/widgets/scrollbar/scrollbar.h>
|
||||
#include <zwidget/systemdialogs/save_file_dialog.h>
|
||||
#include <miniz.h>
|
||||
|
||||
bool ErrorWindow::ExecModal(const std::string& text, const std::string& log)
|
||||
bool ErrorWindow::ExecModal(const std::string& text, const std::string& log, std::vector<uint8_t> minidump)
|
||||
{
|
||||
Size screenSize = GetScreenSize();
|
||||
double windowWidth = 1200.0;
|
||||
double windowHeight = 700.0;
|
||||
|
||||
auto window = std::make_unique<ErrorWindow>();
|
||||
auto window = std::make_unique<ErrorWindow>(std::move(minidump));
|
||||
window->SetText(text, log);
|
||||
window->SetFrameGeometry((screenSize.width - windowWidth) * 0.5, (screenSize.height - windowHeight) * 0.5, windowWidth, windowHeight);
|
||||
window->Show();
|
||||
|
|
@ -23,7 +25,7 @@ bool ErrorWindow::ExecModal(const std::string& text, const std::string& log)
|
|||
return window->Restart;
|
||||
}
|
||||
|
||||
ErrorWindow::ErrorWindow() : Widget(nullptr, WidgetType::Window)
|
||||
ErrorWindow::ErrorWindow(std::vector<uint8_t> initminidump) : Widget(nullptr, WidgetType::Window), minidump(std::move(initminidump))
|
||||
{
|
||||
FStringf caption("Fatal Error - " GAMENAME " %s (%s)", GetVersionString(), GetGitTime());
|
||||
SetWindowTitle(caption.GetChars());
|
||||
|
|
@ -34,13 +36,21 @@ ErrorWindow::ErrorWindow() : Widget(nullptr, WidgetType::Window)
|
|||
|
||||
LogView = new LogViewer(this);
|
||||
ClipboardButton = new PushButton(this);
|
||||
RestartButton = new PushButton(this);
|
||||
|
||||
ClipboardButton->OnClick = [=]() { OnClipboardButtonClicked(); };
|
||||
RestartButton->OnClick = [=]() { OnRestartButtonClicked(); };
|
||||
|
||||
ClipboardButton->SetText("Copy to clipboard");
|
||||
RestartButton->SetText("Restart");
|
||||
|
||||
if (minidump.empty())
|
||||
{
|
||||
RestartButton = new PushButton(this);
|
||||
RestartButton->OnClick = [=]() { OnRestartButtonClicked(); };
|
||||
RestartButton->SetText("Restart");
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveReportButton = new PushButton(this);
|
||||
SaveReportButton->OnClick = [=]() { OnSaveReportButtonClicked(); };
|
||||
SaveReportButton->SetText("Save Report");
|
||||
}
|
||||
|
||||
LogView->SetFocus();
|
||||
}
|
||||
|
|
@ -83,6 +93,37 @@ void ErrorWindow::OnRestartButtonClicked()
|
|||
DisplayWindow::ExitLoop();
|
||||
}
|
||||
|
||||
void ErrorWindow::OnSaveReportButtonClicked()
|
||||
{
|
||||
auto dialog = SaveFileDialog::Create(this);
|
||||
dialog->AddFilter("Crash Report Zip Files", "*.zip");
|
||||
dialog->AddFilter("All Files", "*.*");
|
||||
dialog->SetFilename("CrashReport.zip");
|
||||
dialog->SetDefaultExtension("zip");
|
||||
if (dialog->Show())
|
||||
{
|
||||
std::string filename = dialog->Filename();
|
||||
|
||||
mz_zip_archive zip = {};
|
||||
if (mz_zip_writer_init_heap(&zip, 0, 16 * 1024 * 1024))
|
||||
{
|
||||
mz_zip_writer_add_mem(&zip, "minidump.dmp", minidump.data(), minidump.size(), MZ_DEFAULT_COMPRESSION);
|
||||
mz_zip_writer_add_mem(&zip, "log.txt", clipboardtext.data(), clipboardtext.size(), MZ_DEFAULT_COMPRESSION);
|
||||
}
|
||||
void* buffer = nullptr;
|
||||
size_t buffersize = 0;
|
||||
mz_zip_writer_finalize_heap_archive(&zip, &buffer, &buffersize);
|
||||
mz_zip_writer_end(&zip);
|
||||
|
||||
std::unique_ptr<FileWriter> f(FileWriter::Open(filename.c_str()));
|
||||
if (f)
|
||||
{
|
||||
f->Write(buffer, buffersize);
|
||||
f->Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorWindow::OnClose()
|
||||
{
|
||||
Restart = false;
|
||||
|
|
@ -96,7 +137,10 @@ void ErrorWindow::OnGeometryChanged()
|
|||
|
||||
double y = GetHeight() - 15.0 - ClipboardButton->GetPreferredHeight();
|
||||
ClipboardButton->SetFrameGeometry(20.0, y, 170.0, ClipboardButton->GetPreferredHeight());
|
||||
RestartButton->SetFrameGeometry(GetWidth() - 20.0 - 100.0, y, 100.0, RestartButton->GetPreferredHeight());
|
||||
if (RestartButton)
|
||||
RestartButton->SetFrameGeometry(GetWidth() - 20.0 - 100.0, y, 100.0, RestartButton->GetPreferredHeight());
|
||||
else if (SaveReportButton)
|
||||
SaveReportButton->SetFrameGeometry(GetWidth() - 20.0 - 100.0, y, 100.0, SaveReportButton->GetPreferredHeight());
|
||||
y -= 20.0;
|
||||
|
||||
LogView->SetFrameGeometry(Rect::xywh(0.0, 0.0, w, y));
|
||||
|
|
@ -212,44 +256,44 @@ void LogViewer::OnPaint(Canvas* canvas)
|
|||
}
|
||||
}
|
||||
|
||||
bool LogViewer::OnMouseWheel(const Point& pos, EInputKey key)
|
||||
bool LogViewer::OnMouseWheel(const Point& pos, InputKey key)
|
||||
{
|
||||
if (key == IK_MouseWheelUp)
|
||||
if (key == InputKey::MouseWheelUp)
|
||||
{
|
||||
ScrollUp(4);
|
||||
}
|
||||
else if (key == IK_MouseWheelDown)
|
||||
else if (key == InputKey::MouseWheelDown)
|
||||
{
|
||||
ScrollDown(4);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LogViewer::OnKeyDown(EInputKey key)
|
||||
void LogViewer::OnKeyDown(InputKey key)
|
||||
{
|
||||
if (key == IK_Home)
|
||||
if (key == InputKey::Home)
|
||||
{
|
||||
scrollbar->SetPosition(0.0f);
|
||||
Update();
|
||||
}
|
||||
if (key == IK_End)
|
||||
if (key == InputKey::End)
|
||||
{
|
||||
scrollbar->SetPosition(scrollbar->GetMax());
|
||||
Update();
|
||||
}
|
||||
else if (key == IK_PageUp)
|
||||
else if (key == InputKey::PageUp)
|
||||
{
|
||||
ScrollUp(20);
|
||||
}
|
||||
else if (key == IK_PageDown)
|
||||
else if (key == InputKey::PageDown)
|
||||
{
|
||||
ScrollDown(20);
|
||||
}
|
||||
else if (key == IK_Up)
|
||||
else if (key == InputKey::Up)
|
||||
{
|
||||
ScrollUp(4);
|
||||
}
|
||||
else if (key == IK_Down)
|
||||
else if (key == InputKey::Down)
|
||||
{
|
||||
ScrollDown(4);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ class Scrollbar;
|
|||
class ErrorWindow : public Widget
|
||||
{
|
||||
public:
|
||||
static bool ExecModal(const std::string& text, const std::string& log);
|
||||
static bool ExecModal(const std::string& text, const std::string& log, std::vector<uint8_t> minidump = {});
|
||||
|
||||
ErrorWindow();
|
||||
ErrorWindow(std::vector<uint8_t> minidump);
|
||||
|
||||
bool Restart = false;
|
||||
|
||||
|
|
@ -25,11 +25,14 @@ private:
|
|||
|
||||
void OnClipboardButtonClicked();
|
||||
void OnRestartButtonClicked();
|
||||
void OnSaveReportButtonClicked();
|
||||
|
||||
LogViewer* LogView = nullptr;
|
||||
PushButton* ClipboardButton = nullptr;
|
||||
PushButton* RestartButton = nullptr;
|
||||
PushButton* SaveReportButton = nullptr;
|
||||
|
||||
std::vector<uint8_t> minidump;
|
||||
std::string clipboardtext;
|
||||
};
|
||||
|
||||
|
|
@ -43,8 +46,8 @@ public:
|
|||
protected:
|
||||
void OnPaintFrame(Canvas* canvas) override;
|
||||
void OnPaint(Canvas* canvas) override;
|
||||
bool OnMouseWheel(const Point& pos, EInputKey key) override;
|
||||
void OnKeyDown(EInputKey key) override;
|
||||
bool OnMouseWheel(const Point& pos, InputKey key) override;
|
||||
void OnKeyDown(InputKey key) override;
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
#include <zwidget/core/resourcedata.h>
|
||||
#include <zwidget/core/theme.h>
|
||||
#include "filesystem.h"
|
||||
#include "printf.h"
|
||||
#include "zstring.h"
|
||||
|
|
@ -11,21 +12,31 @@
|
|||
|
||||
FResourceFile* WidgetResources;
|
||||
|
||||
bool IsZWidgetAvailable()
|
||||
{
|
||||
return WidgetResources;
|
||||
}
|
||||
|
||||
void InitWidgetResources(const char* filename)
|
||||
{
|
||||
WidgetResources = FResourceFile::OpenResourceFile(filename);
|
||||
if (!WidgetResources)
|
||||
I_FatalError("Unable to open %s", filename);
|
||||
|
||||
WidgetTheme::SetTheme(std::make_unique<DarkWidgetTheme>());
|
||||
}
|
||||
|
||||
void CloseWidgetResources()
|
||||
{
|
||||
if (WidgetResources) delete WidgetResources;
|
||||
delete WidgetResources;
|
||||
WidgetResources = nullptr;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> LoadFile(const char* name)
|
||||
{
|
||||
if (!WidgetResources)
|
||||
I_FatalError("InitWidgetResources has not been called");
|
||||
|
||||
auto lump = WidgetResources->FindEntry(name);
|
||||
if (lump == -1)
|
||||
I_FatalError("Unable to find %s", name);
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@
|
|||
#include "screenjob.h"
|
||||
#include "startscreen.h"
|
||||
#include "shiftstate.h"
|
||||
#include "common/widgets/errorwindow.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#include "i_system.h" // for SHARE_DIR
|
||||
|
|
@ -3750,6 +3751,15 @@ static int D_DoomMain_Internal (void)
|
|||
|
||||
int GameMain()
|
||||
{
|
||||
// On Windows, prefer the native win32 backend.
|
||||
// On other platforms, use SDL until the other backends are more mature.
|
||||
auto zwidget = DisplayBackend::TryCreateWin32();
|
||||
if (!zwidget)
|
||||
zwidget = DisplayBackend::TryCreateSDL2();
|
||||
if (!zwidget)
|
||||
return -1;
|
||||
DisplayBackend::Set(std::move(zwidget));
|
||||
|
||||
int ret = 0;
|
||||
GameTicRate = TICRATE;
|
||||
I_InitTime();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "version.h"
|
||||
#include "i_interface.h"
|
||||
#include "gstrings.h"
|
||||
#include "c_cvars.h"
|
||||
#include <zwidget/core/resourcedata.h>
|
||||
#include <zwidget/window/window.h>
|
||||
#include <zwidget/widgets/tabwidget/tabwidget.h>
|
||||
|
|
@ -31,10 +32,6 @@ int LauncherWindow::ExecModal(WadStuff* wads, int numwads, int defaultiwad, int*
|
|||
|
||||
LauncherWindow::LauncherWindow(WadStuff* wads, int numwads, int defaultiwad, int* autoloadflags) : Widget(nullptr, WidgetType::Window)
|
||||
{
|
||||
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(GAMENAME);
|
||||
|
||||
Banner = new LauncherBanner(this);
|
||||
|
|
|
|||
|
|
@ -40,16 +40,16 @@ PlayGamePage::PlayGamePage(LauncherWindow* launcher, WadStuff* wads, int numwads
|
|||
GamesList->OnActivated = [=]() { OnGamesListActivated(); };
|
||||
}
|
||||
|
||||
std::string PlayGamePage::GetExtraArgs()
|
||||
{
|
||||
return ParametersEdit->GetText();
|
||||
}
|
||||
|
||||
void PlayGamePage::SetExtraArgs(const std::string& args)
|
||||
{
|
||||
ParametersEdit->SetText(args);
|
||||
}
|
||||
|
||||
std::string PlayGamePage::GetExtraArgs()
|
||||
{
|
||||
return ParametersEdit->GetText();
|
||||
}
|
||||
|
||||
int PlayGamePage::GetSelectedGame()
|
||||
{
|
||||
return GamesList->GetSelectedItem();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ public:
|
|||
|
||||
void SetExtraArgs(const std::string& args);
|
||||
std::string GetExtraArgs();
|
||||
|
||||
int GetSelectedGame();
|
||||
|
||||
private:
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue