Update to latest ZWidget
This commit is contained in:
parent
640a2b2c41
commit
92b08a5c18
78 changed files with 7123 additions and 1261 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
|
||||
|
|
@ -33,7 +49,15 @@ set(ZWIDGET_SOURCES
|
|||
src/widgets/listview/listview.cpp
|
||||
src/widgets/tabwidget/tabwidget.cpp
|
||||
src/window/window.cpp
|
||||
src/systemdialogs/folder_browse_dialog.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
|
||||
)
|
||||
|
|
@ -66,22 +90,62 @@ set(ZWIDGET_INCLUDES
|
|||
include/zwidget/widgets/listview/listview.h
|
||||
include/zwidget/widgets/tabwidget/tabwidget.h
|
||||
include/zwidget/window/window.h
|
||||
include/zwidget/systemdialogs/folder_browse_dialog.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/win32displaywindow.cpp
|
||||
src/window/win32/win32displaywindow.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/.+")
|
||||
|
|
@ -103,6 +167,12 @@ 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/.+")
|
||||
|
|
@ -121,39 +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})
|
||||
set(ZWIDGET_LIBS ${CMAKE_DL_LIBS} -ldl)
|
||||
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")
|
||||
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()
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -22,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);
|
||||
|
|
@ -104,6 +104,7 @@ public:
|
|||
bool IsEnabled();
|
||||
bool IsVisible();
|
||||
bool IsHidden();
|
||||
bool IsFullscreen();
|
||||
|
||||
void SetFocus();
|
||||
void SetEnabled(bool value);
|
||||
|
|
@ -113,8 +114,12 @@ public:
|
|||
void LockCursor();
|
||||
void UnlockCursor();
|
||||
void SetCursor(StandardCursor cursor);
|
||||
void CaptureMouse();
|
||||
void ReleaseMouseCapture();
|
||||
|
||||
void SetPointerCapture();
|
||||
void ReleasePointerCapture();
|
||||
|
||||
void SetModalCapture();
|
||||
void ReleaseModalCapture();
|
||||
|
||||
bool GetKeyState(InputKey key);
|
||||
|
||||
|
|
@ -126,6 +131,9 @@ public:
|
|||
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; }
|
||||
|
|
@ -142,7 +150,14 @@ 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);
|
||||
|
|
@ -220,4 +235,7 @@ private:
|
|||
Widget& operator=(const Widget&) = delete;
|
||||
|
||||
friend class Timer;
|
||||
friend class OpenFileDialog;
|
||||
friend class OpenFolderDialog;
|
||||
friend class SaveFileDialog;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@
|
|||
class Widget;
|
||||
|
||||
/// \brief Displays the system folder browsing dialog
|
||||
class BrowseFolderDialog
|
||||
class OpenFolderDialog
|
||||
{
|
||||
public:
|
||||
/// \brief Constructs an browse folder dialog.
|
||||
static std::unique_ptr<BrowseFolderDialog> Create(Widget*owner);
|
||||
static std::unique_ptr<OpenFolderDialog> Create(Widget*owner);
|
||||
|
||||
virtual ~BrowseFolderDialog() = default;
|
||||
virtual ~OpenFolderDialog() = default;
|
||||
|
||||
/// \brief Get the full path of the directory selected.
|
||||
virtual std::string SelectedPath() const = 0;
|
||||
|
|
@ -4,12 +4,20 @@
|
|||
#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;
|
||||
|
|
@ -19,4 +27,5 @@ protected:
|
|||
|
||||
private:
|
||||
std::shared_ptr<Image> image;
|
||||
ImageBoxMode mode = ImageBoxMode::Center;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public:
|
|||
ListView(Widget* parent = nullptr);
|
||||
|
||||
void AddItem(const std::string& text);
|
||||
void RemoveItem(int index);
|
||||
int GetSelectedItem() const { return selectedItem; }
|
||||
void SetSelectedItem(int index);
|
||||
void ScrollToItem(int index);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,13 +21,22 @@ public:
|
|||
|
||||
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;
|
||||
};
|
||||
|
|
@ -70,10 +79,13 @@ public:
|
|||
double GetPreferredWidth() const;
|
||||
double GetPreferredHeight() const;
|
||||
|
||||
void SetSelected(MenuItem* item);
|
||||
|
||||
protected:
|
||||
void OnGeometryChanged() override;
|
||||
|
||||
std::function<void()> onCloseMenu;
|
||||
MenuItem* selectedItem = nullptr;
|
||||
|
||||
friend class Menubar;
|
||||
};
|
||||
|
|
@ -81,19 +93,29 @@ protected:
|
|||
class MenuItem : public Widget
|
||||
{
|
||||
public:
|
||||
MenuItem(Widget* parent);
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 = {};
|
||||
};
|
||||
|
|
@ -4,9 +4,28 @@
|
|||
#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
|
||||
{
|
||||
|
|
@ -93,6 +112,17 @@ enum class InputKey : uint32_t
|
|||
NoName, PA1, OEMClear
|
||||
};
|
||||
|
||||
enum class RenderAPI
|
||||
{
|
||||
Unspecified,
|
||||
Bitmap,
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
D3D11,
|
||||
D3D12,
|
||||
Metal
|
||||
};
|
||||
|
||||
class DisplayWindow;
|
||||
|
||||
class DisplayWindowHost
|
||||
|
|
@ -119,7 +149,7 @@ public:
|
|||
class DisplayWindow
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner);
|
||||
static std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI);
|
||||
|
||||
static void ProcessEvents();
|
||||
static void RunLoop();
|
||||
|
|
@ -140,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;
|
||||
|
|
@ -171,4 +202,42 @@ public:
|
|||
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;
|
||||
};
|
||||
|
|
@ -18,13 +18,7 @@
|
|||
#define USE_SSE2
|
||||
#endif
|
||||
|
||||
class CanvasTexture
|
||||
{
|
||||
public:
|
||||
int Width = 0;
|
||||
int Height = 0;
|
||||
std::vector<uint32_t> Data;
|
||||
};
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CanvasGlyph
|
||||
{
|
||||
|
|
@ -46,78 +40,12 @@ public:
|
|||
class CanvasFont
|
||||
{
|
||||
public:
|
||||
CanvasFont(const std::string& fontname, double height, std::vector<uint8_t> data) : fontname(fontname), height(height)
|
||||
{
|
||||
auto tdata = std::make_shared<TrueTypeFontFileData>(std::move(data));
|
||||
ttf = std::make_unique<TrueTypeFont>(tdata);
|
||||
textmetrics = ttf->GetTextMetrics(height);
|
||||
}
|
||||
CanvasFont(const std::string& fontname, double height, std::vector<uint8_t> data);
|
||||
~CanvasFont();
|
||||
|
||||
~CanvasFont()
|
||||
{
|
||||
}
|
||||
|
||||
CanvasGlyph* getGlyph(uint32_t utfchar)
|
||||
{
|
||||
uint32_t glyphIndex = ttf->GetGlyphIndex(utfchar);
|
||||
if (glyphIndex == 0) return nullptr;
|
||||
|
||||
auto& glyph = glyphs[glyphIndex];
|
||||
if (glyph)
|
||||
return glyph.get();
|
||||
|
||||
glyph = std::make_unique<CanvasGlyph>();
|
||||
|
||||
TrueTypeGlyph ttfglyph = ttf->LoadGlyph(glyphIndex, height);
|
||||
|
||||
// Create final subpixel version
|
||||
int w = ttfglyph.width;
|
||||
int h = ttfglyph.height;
|
||||
int destwidth = (w + 2) / 3;
|
||||
auto texture = std::make_shared<CanvasTexture>();
|
||||
texture->Width = destwidth;
|
||||
texture->Height = h;
|
||||
texture->Data.resize(destwidth * h);
|
||||
|
||||
uint8_t* grayscale = ttfglyph.grayscale.get();
|
||||
uint32_t* dest = (uint32_t*)texture->Data.data();
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
uint8_t* sline = grayscale + y * w;
|
||||
uint32_t* dline = dest + y * destwidth;
|
||||
for (int x = 0; x < w; x += 3)
|
||||
{
|
||||
uint32_t values[5] =
|
||||
{
|
||||
x > 0 ? sline[x - 1] : 0U,
|
||||
sline[x],
|
||||
x + 1 < w ? sline[x + 1] : 0U,
|
||||
x + 2 < w ? sline[x + 2] : 0U,
|
||||
x + 3 < w ? sline[x + 3] : 0U
|
||||
};
|
||||
|
||||
uint32_t red = (values[0] + values[1] + values[1] + values[2] + 2) >> 2;
|
||||
uint32_t green = (values[1] + values[2] + values[2] + values[3] + 2) >> 2;
|
||||
uint32_t blue = (values[2] + values[3] + values[3] + values[4] + 2) >> 2;
|
||||
uint32_t alpha = (red | green | blue) ? 255 : 0;
|
||||
|
||||
*(dline++) = (alpha << 24) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
}
|
||||
|
||||
glyph->u = 0.0;
|
||||
glyph->v = 0.0;
|
||||
glyph->uvwidth = destwidth;
|
||||
glyph->uvheight = h;
|
||||
glyph->texture = std::move(texture);
|
||||
|
||||
glyph->metrics.advanceWidth = (ttfglyph.advanceWidth + 2) / 3;
|
||||
glyph->metrics.leftSideBearing = (ttfglyph.leftSideBearing + 2) / 3;
|
||||
glyph->metrics.yOffset = ttfglyph.yOffset;
|
||||
|
||||
return glyph.get();
|
||||
}
|
||||
CanvasGlyph* getGlyph(Canvas* canvas, uint32_t utfchar);
|
||||
|
||||
private:
|
||||
std::unique_ptr<TrueTypeFont> ttf;
|
||||
|
||||
std::string fontname;
|
||||
|
|
@ -125,6 +53,8 @@ public:
|
|||
|
||||
TrueTypeTextMetrics textmetrics;
|
||||
std::unordered_map<uint32_t, std::unique_ptr<CanvasGlyph>> glyphs;
|
||||
|
||||
friend class CanvasFontGroup;
|
||||
};
|
||||
|
||||
class CanvasFontGroup
|
||||
|
|
@ -135,144 +65,164 @@ public:
|
|||
std::unique_ptr<CanvasFont> font;
|
||||
std::string language;
|
||||
};
|
||||
CanvasFontGroup(const std::string& fontname, double height) : height(height)
|
||||
{
|
||||
auto fontdata = LoadWidgetFontData(fontname);
|
||||
fonts.resize(fontdata.size());
|
||||
for (size_t i = 0; i < fonts.size(); i++)
|
||||
{
|
||||
fonts[i].font = std::make_unique<CanvasFont>(fontname, height, fontdata[i].fontdata);
|
||||
fonts[i].language = fontdata[i].language;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasGlyph* getGlyph(uint32_t utfchar, const char* lang = nullptr)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (auto& fd : fonts)
|
||||
{
|
||||
if (i == 1 || lang == nullptr || *lang == 0 || fd.language.empty() || fd.language == lang)
|
||||
{
|
||||
auto g = fd.font->getGlyph(utfchar);
|
||||
if (g) return g;
|
||||
}
|
||||
}
|
||||
}
|
||||
CanvasFontGroup(const std::string& fontname, double height);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TrueTypeTextMetrics& GetTextMetrics()
|
||||
{
|
||||
return fonts[0].font->textmetrics;
|
||||
}
|
||||
CanvasGlyph* getGlyph(Canvas* canvas, uint32_t utfchar, const char* lang = nullptr);
|
||||
TrueTypeTextMetrics& GetTextMetrics();
|
||||
|
||||
double height;
|
||||
std::vector<SingleFont> fonts;
|
||||
|
||||
};
|
||||
|
||||
class BitmapCanvas : public Canvas
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CanvasFont::CanvasFont(const std::string& fontname, double height, std::vector<uint8_t> data) : fontname(fontname), height(height)
|
||||
{
|
||||
public:
|
||||
BitmapCanvas(DisplayWindow* window);
|
||||
~BitmapCanvas();
|
||||
auto tdata = std::make_shared<TrueTypeFontFileData>(std::move(data));
|
||||
ttf = std::make_unique<TrueTypeFont>(tdata);
|
||||
textmetrics = ttf->GetTextMetrics(height);
|
||||
}
|
||||
|
||||
void begin(const Colorf& color) override;
|
||||
void end() override;
|
||||
CanvasFont::~CanvasFont()
|
||||
{
|
||||
}
|
||||
|
||||
void begin3d() override;
|
||||
void end3d() override;
|
||||
CanvasGlyph* CanvasFont::getGlyph(Canvas* canvas, uint32_t utfchar)
|
||||
{
|
||||
uint32_t glyphIndex = ttf->GetGlyphIndex(utfchar);
|
||||
if (glyphIndex == 0) return nullptr;
|
||||
|
||||
Point getOrigin() override;
|
||||
void setOrigin(const Point& origin) override;
|
||||
auto& glyph = glyphs[glyphIndex];
|
||||
if (glyph)
|
||||
return glyph.get();
|
||||
|
||||
void pushClip(const Rect& box) override;
|
||||
void popClip() override;
|
||||
glyph = std::make_unique<CanvasGlyph>();
|
||||
|
||||
void fillRect(const Rect& box, const Colorf& color) override;
|
||||
void line(const Point& p0, const Point& p1, const Colorf& color) override;
|
||||
TrueTypeGlyph ttfglyph = ttf->LoadGlyph(glyphIndex, height);
|
||||
|
||||
void drawText(const Point& pos, const Colorf& color, const std::string& text) override;
|
||||
Rect measureText(const std::string& text) override;
|
||||
VerticalTextPosition verticalTextAlign() override;
|
||||
// Create final subpixel version
|
||||
int w = ttfglyph.width;
|
||||
int h = ttfglyph.height;
|
||||
int destwidth = (w + 2) / 3;
|
||||
auto texture = std::make_shared<CanvasTexture>();
|
||||
std::vector<uint32_t> data(destwidth * h);
|
||||
|
||||
void drawText(const std::shared_ptr<Font>& font, const Point& pos, const std::string& text, const Colorf& color) override { drawText(pos, color, text); }
|
||||
void drawTextEllipsis(const std::shared_ptr<Font>& font, const Point& pos, const Rect& clipBox, const std::string& text, const Colorf& color) override { drawText(pos, color, text); }
|
||||
Rect measureText(const std::shared_ptr<Font>& font, const std::string& text) override { return measureText(text); }
|
||||
FontMetrics getFontMetrics(const std::shared_ptr<Font>& font) override
|
||||
uint8_t* grayscale = ttfglyph.grayscale.get();
|
||||
uint32_t* dest = data.data();
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
VerticalTextPosition vtp = verticalTextAlign();
|
||||
FontMetrics metrics;
|
||||
metrics.external_leading = vtp.top;
|
||||
metrics.ascent = vtp.baseline - vtp.top;
|
||||
metrics.descent = vtp.bottom - vtp.baseline;
|
||||
metrics.height = metrics.ascent + metrics.descent;
|
||||
return metrics;
|
||||
uint8_t* sline = grayscale + y * w;
|
||||
uint32_t* dline = dest + y * destwidth;
|
||||
for (int x = 0; x < w; x += 3)
|
||||
{
|
||||
uint32_t values[5] =
|
||||
{
|
||||
x > 0 ? sline[x - 1] : 0U,
|
||||
sline[x],
|
||||
x + 1 < w ? sline[x + 1] : 0U,
|
||||
x + 2 < w ? sline[x + 2] : 0U,
|
||||
x + 3 < w ? sline[x + 3] : 0U
|
||||
};
|
||||
|
||||
uint32_t red = (values[0] + values[1] + values[1] + values[2] + 2) >> 2;
|
||||
uint32_t green = (values[1] + values[2] + values[2] + values[3] + 2) >> 2;
|
||||
uint32_t blue = (values[2] + values[3] + values[3] + values[4] + 2) >> 2;
|
||||
uint32_t alpha = (red | green | blue) ? 255 : 0;
|
||||
|
||||
*(dline++) = (alpha << 24) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
}
|
||||
int getCharacterIndex(const std::shared_ptr<Font>& font, const std::string& text, const Point& hitPoint) override { return 0; }
|
||||
|
||||
void drawImage(const std::shared_ptr<Image>& image, const Point& pos) override;
|
||||
glyph->u = 0.0;
|
||||
glyph->v = 0.0;
|
||||
glyph->uvwidth = destwidth;
|
||||
glyph->uvheight = h;
|
||||
glyph->texture = canvas->createTexture(destwidth, h, data.data(), ImageFormat::B8G8R8A8);
|
||||
|
||||
void drawLineUnclipped(const Point& p0, const Point& p1, const Colorf& color);
|
||||
glyph->metrics.advanceWidth = (ttfglyph.advanceWidth + 2) / 3;
|
||||
glyph->metrics.leftSideBearing = (ttfglyph.leftSideBearing + 2) / 3;
|
||||
glyph->metrics.yOffset = ttfglyph.yOffset;
|
||||
|
||||
void fillTile(float x, float y, float width, float height, Colorf color);
|
||||
void drawTile(CanvasTexture* texture, float x, float y, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color);
|
||||
void drawGlyph(CanvasTexture* texture, float x, float y, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color);
|
||||
return glyph.get();
|
||||
}
|
||||
|
||||
int getClipMinX() const;
|
||||
int getClipMinY() const;
|
||||
int getClipMaxX() const;
|
||||
int getClipMaxY() const;
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setLanguage(const char* lang) { language = lang; }
|
||||
|
||||
std::unique_ptr<CanvasTexture> createTexture(int width, int height, const void* pixels, ImageFormat format = ImageFormat::B8G8R8A8);
|
||||
|
||||
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;
|
||||
|
||||
std::unique_ptr<CanvasFontGroup> font;
|
||||
std::unique_ptr<CanvasTexture> whiteTexture;
|
||||
|
||||
Point origin;
|
||||
double uiscale = 1.0f;
|
||||
std::vector<Rect> clipStack;
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
std::vector<uint32_t> pixels;
|
||||
|
||||
std::unordered_map<std::shared_ptr<Image>, std::unique_ptr<CanvasTexture>> imageTextures;
|
||||
std::string language;
|
||||
};
|
||||
|
||||
BitmapCanvas::BitmapCanvas(DisplayWindow* window) : window(window)
|
||||
CanvasFontGroup::CanvasFontGroup(const std::string& fontname, double height) : height(height)
|
||||
{
|
||||
auto fontdata = LoadWidgetFontData(fontname);
|
||||
fonts.resize(fontdata.size());
|
||||
for (size_t i = 0; i < fonts.size(); i++)
|
||||
{
|
||||
fonts[i].font = std::make_unique<CanvasFont>(fontname, height, fontdata[i].fontdata);
|
||||
fonts[i].language = fontdata[i].language;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasGlyph* CanvasFontGroup::getGlyph(Canvas* canvas, uint32_t utfchar, const char* lang)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (auto& fd : fonts)
|
||||
{
|
||||
if (i == 1 || lang == nullptr || *lang == 0 || fd.language.empty() || fd.language == lang)
|
||||
{
|
||||
auto g = fd.font->getGlyph(canvas, utfchar);
|
||||
if (g) return g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TrueTypeTextMetrics& CanvasFontGroup::GetTextMetrics()
|
||||
{
|
||||
return fonts[0].font->textmetrics;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Canvas::Canvas()
|
||||
{
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
}
|
||||
|
||||
void Canvas::attach(DisplayWindow* newWindow)
|
||||
{
|
||||
window = newWindow;
|
||||
uiscale = window->GetDpiScale();
|
||||
uint32_t white = 0xffffffff;
|
||||
whiteTexture = createTexture(1, 1, &white);
|
||||
font = std::make_unique<CanvasFontGroup>("NotoSans", 13.0 * uiscale);
|
||||
}
|
||||
|
||||
BitmapCanvas::~BitmapCanvas()
|
||||
void Canvas::detach()
|
||||
{
|
||||
}
|
||||
|
||||
Point BitmapCanvas::getOrigin()
|
||||
void Canvas::begin(const Colorf& color)
|
||||
{
|
||||
uiscale = window->GetDpiScale();
|
||||
width = window->GetPixelWidth();
|
||||
height = window->GetPixelHeight();
|
||||
}
|
||||
|
||||
Point Canvas::getOrigin()
|
||||
{
|
||||
return origin;
|
||||
}
|
||||
|
||||
void BitmapCanvas::setOrigin(const Point& newOrigin)
|
||||
void Canvas::setOrigin(const Point& newOrigin)
|
||||
{
|
||||
origin = newOrigin;
|
||||
}
|
||||
|
||||
void BitmapCanvas::pushClip(const Rect& box)
|
||||
void Canvas::pushClip(const Rect& box)
|
||||
{
|
||||
if (!clipStack.empty())
|
||||
{
|
||||
|
|
@ -299,17 +249,17 @@ void BitmapCanvas::pushClip(const Rect& box)
|
|||
}
|
||||
}
|
||||
|
||||
void BitmapCanvas::popClip()
|
||||
void Canvas::popClip()
|
||||
{
|
||||
clipStack.pop_back();
|
||||
}
|
||||
|
||||
void BitmapCanvas::fillRect(const Rect& box, const Colorf& color)
|
||||
void Canvas::fillRect(const Rect& box, const Colorf& color)
|
||||
{
|
||||
fillTile((float)((origin.x + box.x) * uiscale), (float)((origin.y + box.y) * uiscale), (float)(box.width * uiscale), (float)(box.height * uiscale), color);
|
||||
}
|
||||
|
||||
void BitmapCanvas::drawImage(const std::shared_ptr<Image>& image, const Point& pos)
|
||||
void Canvas::drawImage(const std::shared_ptr<Image>& image, const Point& pos)
|
||||
{
|
||||
auto& texture = imageTextures[image];
|
||||
if (!texture)
|
||||
|
|
@ -320,7 +270,18 @@ void BitmapCanvas::drawImage(const std::shared_ptr<Image>& image, const Point& p
|
|||
drawTile(texture.get(), (float)((origin.x + pos.x) * uiscale), (float)((origin.y + pos.y) * uiscale), (float)(texture->Width * uiscale), (float)(texture->Height * uiscale), 0.0, 0.0, (float)texture->Width, (float)texture->Height, color);
|
||||
}
|
||||
|
||||
void BitmapCanvas::line(const Point& p0, const Point& p1, const Colorf& color)
|
||||
void Canvas::drawImage(const std::shared_ptr<Image>& image, const Rect& box)
|
||||
{
|
||||
auto& texture = imageTextures[image];
|
||||
if (!texture)
|
||||
{
|
||||
texture = createTexture(image->GetWidth(), image->GetHeight(), image->GetData(), image->GetFormat());
|
||||
}
|
||||
Colorf color(1.0f, 1.0f, 1.0f);
|
||||
drawTile(texture.get(), (float)((origin.x + box.x) * uiscale), (float)((origin.y + box.y) * uiscale), (float)(box.width * uiscale), (float)(box.height * uiscale), 0.0, 0.0, (float)texture->Width, (float)texture->Height, color);
|
||||
}
|
||||
|
||||
void Canvas::line(const Point& p0, const Point& p1, const Colorf& color)
|
||||
{
|
||||
double x0 = origin.x + p0.x;
|
||||
double y0 = origin.y + p0.y;
|
||||
|
|
@ -407,7 +368,7 @@ void BitmapCanvas::line(const Point& p0, const Point& p1, const Colorf& color)
|
|||
}
|
||||
}
|
||||
|
||||
void BitmapCanvas::drawText(const Point& pos, const Colorf& color, const std::string& text)
|
||||
void Canvas::drawText(const Point& pos, const Colorf& color, const std::string& text)
|
||||
{
|
||||
double x = std::round((origin.x + pos.x) * uiscale);
|
||||
double y = std::round((origin.y + pos.y) * uiscale);
|
||||
|
|
@ -415,10 +376,10 @@ void BitmapCanvas::drawText(const Point& pos, const Colorf& color, const std::st
|
|||
UTF8Reader reader(text.data(), text.size());
|
||||
while (!reader.is_end())
|
||||
{
|
||||
CanvasGlyph* glyph = font->getGlyph(reader.character(), language.c_str());
|
||||
CanvasGlyph* glyph = font->getGlyph(this, reader.character(), language.c_str());
|
||||
if (!glyph || !glyph->texture)
|
||||
{
|
||||
glyph = font->getGlyph(32);
|
||||
glyph = font->getGlyph(this, 32);
|
||||
}
|
||||
|
||||
if (glyph->texture)
|
||||
|
|
@ -433,7 +394,22 @@ void BitmapCanvas::drawText(const Point& pos, const Colorf& color, const std::st
|
|||
}
|
||||
}
|
||||
|
||||
Rect BitmapCanvas::measureText(const std::string& text)
|
||||
void Canvas::drawText(const std::shared_ptr<Font>& font, const Point& pos, const std::string& text, const Colorf& color)
|
||||
{
|
||||
drawText(pos, color, text);
|
||||
}
|
||||
|
||||
void Canvas::drawTextEllipsis(const std::shared_ptr<Font>& font, const Point& pos, const Rect& clipBox, const std::string& text, const Colorf& color)
|
||||
{
|
||||
drawText(pos, color, text);
|
||||
}
|
||||
|
||||
Rect Canvas::measureText(const std::shared_ptr<Font>& font, const std::string& text)
|
||||
{
|
||||
return measureText(text);
|
||||
}
|
||||
|
||||
Rect Canvas::measureText(const std::string& text)
|
||||
{
|
||||
double x = 0.0;
|
||||
double y = font->GetTextMetrics().ascender - font->GetTextMetrics().descender;
|
||||
|
|
@ -441,10 +417,10 @@ Rect BitmapCanvas::measureText(const std::string& text)
|
|||
UTF8Reader reader(text.data(), text.size());
|
||||
while (!reader.is_end())
|
||||
{
|
||||
CanvasGlyph* glyph = font->getGlyph(reader.character(), language.c_str());
|
||||
CanvasGlyph* glyph = font->getGlyph(this, reader.character(), language.c_str());
|
||||
if (!glyph || !glyph->texture)
|
||||
{
|
||||
glyph = font->getGlyph(32);
|
||||
glyph = font->getGlyph(this, 32);
|
||||
}
|
||||
|
||||
x += std::round(glyph->metrics.advanceWidth);
|
||||
|
|
@ -454,7 +430,23 @@ Rect BitmapCanvas::measureText(const std::string& text)
|
|||
return Rect::xywh(0.0, 0.0, x / uiscale, y / uiscale);
|
||||
}
|
||||
|
||||
VerticalTextPosition BitmapCanvas::verticalTextAlign()
|
||||
FontMetrics Canvas::getFontMetrics(const std::shared_ptr<Font>& font)
|
||||
{
|
||||
VerticalTextPosition vtp = verticalTextAlign();
|
||||
FontMetrics metrics;
|
||||
metrics.external_leading = vtp.top;
|
||||
metrics.ascent = vtp.baseline - vtp.top;
|
||||
metrics.descent = vtp.bottom - vtp.baseline;
|
||||
metrics.height = metrics.ascent + metrics.descent;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
int Canvas::getCharacterIndex(const std::shared_ptr<Font>& font, const std::string& text, const Point& hitPoint)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
VerticalTextPosition Canvas::verticalTextAlign()
|
||||
{
|
||||
VerticalTextPosition align;
|
||||
align.top = 0.0f;
|
||||
|
|
@ -464,9 +456,70 @@ VerticalTextPosition BitmapCanvas::verticalTextAlign()
|
|||
return align;
|
||||
}
|
||||
|
||||
void Canvas::drawLineUnclipped(const Point& p0, const Point& p1, const Colorf& color)
|
||||
{
|
||||
if (p0.x == p1.x)
|
||||
{
|
||||
fillTile((float)((p0.x - 0.5) * uiscale), (float)(p0.y * uiscale), (float)uiscale, (float)((p1.y - p0.y) * uiscale), color);
|
||||
}
|
||||
else if (p0.y == p1.y)
|
||||
{
|
||||
fillTile((float)(p0.x * uiscale), (float)((p0.y - 0.5) * uiscale), (float)((p1.x - p0.x) * uiscale), (float)uiscale, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawLineAntialiased((float)(p0.x * uiscale), (float)(p0.y * uiscale), (float)(p1.x * uiscale), (float)(p1.y * uiscale), color);
|
||||
}
|
||||
}
|
||||
|
||||
int Canvas::getClipMinX() const
|
||||
{
|
||||
return clipStack.empty() ? 0 : (int)std::round(std::max(clipStack.back().x * uiscale, 0.0));
|
||||
}
|
||||
|
||||
int Canvas::getClipMinY() const
|
||||
{
|
||||
return clipStack.empty() ? 0 : (int)std::round(std::max(clipStack.back().y * uiscale, 0.0));
|
||||
}
|
||||
|
||||
int Canvas::getClipMaxX() const
|
||||
{
|
||||
return clipStack.empty() ? width : (int)std::round(std::min((clipStack.back().x + clipStack.back().width) * uiscale, (double)width));
|
||||
}
|
||||
|
||||
int Canvas::getClipMaxY() const
|
||||
{
|
||||
return clipStack.empty() ? height : (int)std::round(std::min((clipStack.back().y + clipStack.back().height) * uiscale, (double)height));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class BitmapTexture : public CanvasTexture
|
||||
{
|
||||
public:
|
||||
std::vector<uint32_t> Data;
|
||||
};
|
||||
|
||||
class BitmapCanvas : public Canvas
|
||||
{
|
||||
public:
|
||||
void begin(const Colorf& color) override;
|
||||
void end() override;
|
||||
|
||||
void fillTile(float x, float y, float width, float height, Colorf color) override;
|
||||
void drawTile(CanvasTexture* texture, float x, float y, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color) override;
|
||||
void drawGlyph(CanvasTexture* texture, float x, float y, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color) override;
|
||||
void drawLineAntialiased(float x0, float y0, float x1, float y1, Colorf color) override;
|
||||
void plot(float x, float y, float alpha, const Colorf& color);
|
||||
|
||||
std::unique_ptr<CanvasTexture> createTexture(int width, int height, const void* pixels, ImageFormat format = ImageFormat::B8G8R8A8) override;
|
||||
|
||||
std::vector<uint32_t> pixels;
|
||||
};
|
||||
|
||||
std::unique_ptr<CanvasTexture> BitmapCanvas::createTexture(int width, int height, const void* pixels, ImageFormat format)
|
||||
{
|
||||
auto texture = std::make_unique<CanvasTexture>();
|
||||
auto texture = std::make_unique<BitmapTexture>();
|
||||
texture->Width = width;
|
||||
texture->Height = height;
|
||||
texture->Data.resize(width * height);
|
||||
|
|
@ -491,40 +544,119 @@ std::unique_ptr<CanvasTexture> BitmapCanvas::createTexture(int width, int height
|
|||
return texture;
|
||||
}
|
||||
|
||||
void BitmapCanvas::drawLineUnclipped(const Point& p0, const Point& p1, const Colorf& color)
|
||||
void BitmapCanvas::plot(float x, float y, float alpha, const Colorf& color)
|
||||
{
|
||||
if (p0.x == p1.x)
|
||||
int xx = (int)x;
|
||||
int yy = (int)y;
|
||||
|
||||
int dwidth = width;
|
||||
int dheight = height;
|
||||
uint32_t* dest = pixels.data() + xx + yy * dwidth;
|
||||
|
||||
uint32_t cred = (int32_t)clamp(color.r * 256.0f, 0.0f, 256.0f);
|
||||
uint32_t cgreen = (int32_t)clamp(color.g * 256.0f, 0.0f, 256.0f);
|
||||
uint32_t cblue = (int32_t)clamp(color.b * 256.0f, 0.0f, 256.0f);
|
||||
uint32_t calpha = (int32_t)clamp(color.a * alpha * 256.0f, 0.0f, 256.0f);
|
||||
uint32_t invalpha = 256 - calpha;
|
||||
|
||||
uint32_t dpixel = *dest;
|
||||
uint32_t dalpha = dpixel >> 24;
|
||||
uint32_t dred = (dpixel >> 16) & 0xff;
|
||||
uint32_t dgreen = (dpixel >> 8) & 0xff;
|
||||
uint32_t dblue = dpixel & 0xff;
|
||||
|
||||
// dest.rgba = color.rgba + dest.rgba * (1-color.a)
|
||||
uint32_t a = (calpha * calpha + dalpha * invalpha + 127) >> 8;
|
||||
uint32_t r = (cred * calpha + dred * invalpha + 127) >> 8;
|
||||
uint32_t g = (cgreen * calpha + dgreen * invalpha + 127) >> 8;
|
||||
uint32_t b = (cblue * calpha + dblue * invalpha + 127) >> 8;
|
||||
*dest = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
static float fpart(float x)
|
||||
{
|
||||
return x - std::floor(x);
|
||||
}
|
||||
|
||||
static float rfpart(float x)
|
||||
{
|
||||
return 1 - fpart(x);
|
||||
}
|
||||
|
||||
void BitmapCanvas::drawLineAntialiased(float x0, float y0, float x1, float y1, Colorf color)
|
||||
{
|
||||
bool steep = std::abs(y1 - y0) > std::abs(x1 - x0);
|
||||
|
||||
if (steep)
|
||||
{
|
||||
fillTile((float)((p0.x - 0.5) * uiscale), (float)(p0.y * uiscale), (float)uiscale, (float)((p1.y - p0.y) * uiscale), color);
|
||||
std::swap(x0, y0);
|
||||
std::swap(x1, y1);
|
||||
}
|
||||
else if (p0.y == p1.y)
|
||||
|
||||
if (x0 > x1)
|
||||
{
|
||||
fillTile((float)(p0.x * uiscale), (float)((p0.y - 0.5) * uiscale), (float)((p1.x - p0.x) * uiscale), (float)uiscale, color);
|
||||
std::swap(x0, x1);
|
||||
std::swap(y0, y1);
|
||||
}
|
||||
|
||||
float dx = x1 - x0;
|
||||
float dy = y1 - y0;
|
||||
float gradient = (dx == 0.0f) ? 1.0f : dy / dx;
|
||||
|
||||
// handle first endpoint
|
||||
float xend = std::round(x0);
|
||||
float yend = y0 + gradient * (xend - x0);
|
||||
float xgap = rfpart(x0 + 0.5f);
|
||||
float xpxl1 = xend; // this will be used in the main loop
|
||||
float ypxl1 = std::floor(yend);
|
||||
if (steep)
|
||||
{
|
||||
plot(ypxl1, xpxl1, rfpart(yend) * xgap, color);
|
||||
plot(ypxl1 + 1, xpxl1, fpart(yend) * xgap, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
// To do: draw line using bresenham
|
||||
plot(xpxl1, ypxl1, rfpart(yend) * xgap, color);
|
||||
plot(xpxl1, ypxl1 + 1, fpart(yend) * xgap, color);
|
||||
}
|
||||
}
|
||||
float intery = yend + gradient; // first y-intersection for the main loop
|
||||
|
||||
int BitmapCanvas::getClipMinX() const
|
||||
{
|
||||
return clipStack.empty() ? 0 : (int)std::max(clipStack.back().x * uiscale, 0.0);
|
||||
}
|
||||
// handle second endpoint
|
||||
xend = std::floor(x1 + 0.5f);
|
||||
yend = y1 + gradient * (xend - x1);
|
||||
xgap = fpart(x1 + 0.5f);
|
||||
float xpxl2 = xend; // this will be used in the main loop
|
||||
float ypxl2 = std::floor(yend);
|
||||
if (steep)
|
||||
{
|
||||
plot(ypxl2, xpxl2, rfpart(yend) * xgap, color);
|
||||
plot(ypxl2 + 1.0f, xpxl2, fpart(yend) * xgap, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
plot(xpxl2, ypxl2, rfpart(yend) * xgap, color);
|
||||
plot(xpxl2, ypxl2 + 1.0f, fpart(yend) * xgap, color);
|
||||
}
|
||||
|
||||
int BitmapCanvas::getClipMinY() const
|
||||
{
|
||||
return clipStack.empty() ? 0 : (int)std::max(clipStack.back().y * uiscale, 0.0);
|
||||
}
|
||||
|
||||
int BitmapCanvas::getClipMaxX() const
|
||||
{
|
||||
return clipStack.empty() ? width : (int)std::min((clipStack.back().x + clipStack.back().width) * uiscale, (double)width);
|
||||
}
|
||||
|
||||
int BitmapCanvas::getClipMaxY() const
|
||||
{
|
||||
return clipStack.empty() ? height : (int)std::min((clipStack.back().y + clipStack.back().height) * uiscale, (double)height);
|
||||
// main loop
|
||||
if (steep)
|
||||
{
|
||||
for (float x = xpxl1 + 1.0f; x <= xpxl2 - 1.0f; x++)
|
||||
{
|
||||
plot(std::floor(intery), x, rfpart(intery), color);
|
||||
plot(std::floor(intery) + 1.0f, x, fpart(intery), color);
|
||||
intery = intery + gradient;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (float x = xpxl1 + 1.0f; x <= xpxl2 - 1.0f; x++)
|
||||
{
|
||||
plot(x, std::floor(intery), rfpart(intery), color);
|
||||
plot(x, std::floor(intery) + 1, fpart(intery), color);
|
||||
intery = intery + gradient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapCanvas::fillTile(float left, float top, float width, float height, Colorf color)
|
||||
|
|
@ -632,11 +764,12 @@ void BitmapCanvas::fillTile(float left, float top, float width, float height, Co
|
|||
}
|
||||
}
|
||||
|
||||
void BitmapCanvas::drawTile(CanvasTexture* texture, float left, float top, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color)
|
||||
void BitmapCanvas::drawTile(CanvasTexture* tex, float left, float top, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color)
|
||||
{
|
||||
if (width <= 0.0f || height <= 0.0f || color.a <= 0.0f)
|
||||
return;
|
||||
|
||||
auto texture = static_cast<BitmapTexture*>(tex);
|
||||
int swidth = texture->Width;
|
||||
int sheight = texture->Height;
|
||||
const uint32_t* src = texture->Data.data();
|
||||
|
|
@ -740,11 +873,12 @@ void BitmapCanvas::drawTile(CanvasTexture* texture, float left, float top, float
|
|||
}
|
||||
}
|
||||
|
||||
void BitmapCanvas::drawGlyph(CanvasTexture* texture, float left, float top, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color)
|
||||
void BitmapCanvas::drawGlyph(CanvasTexture* tex, float left, float top, float width, float height, float u, float v, float uvwidth, float uvheight, Colorf color)
|
||||
{
|
||||
if (width <= 0.0f || height <= 0.0f)
|
||||
return;
|
||||
|
||||
auto texture = static_cast<BitmapTexture*>(tex);
|
||||
int swidth = texture->Width;
|
||||
int sheight = texture->Height;
|
||||
const uint32_t* src = texture->Data.data();
|
||||
|
|
@ -926,15 +1060,13 @@ void BitmapCanvas::drawGlyph(CanvasTexture* texture, float left, float top, floa
|
|||
|
||||
void BitmapCanvas::begin(const Colorf& color)
|
||||
{
|
||||
uiscale = window->GetDpiScale();
|
||||
Canvas::begin(color);
|
||||
|
||||
uint32_t r = (int32_t)clamp(color.r * 255.0f, 0.0f, 255.0f);
|
||||
uint32_t g = (int32_t)clamp(color.g * 255.0f, 0.0f, 255.0f);
|
||||
uint32_t b = (int32_t)clamp(color.b * 255.0f, 0.0f, 255.0f);
|
||||
uint32_t a = (int32_t)clamp(color.a * 255.0f, 0.0f, 255.0f);
|
||||
uint32_t bgcolor = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
width = window->GetPixelWidth();
|
||||
height = window->GetPixelHeight();
|
||||
pixels.clear();
|
||||
pixels.resize(width * height, bgcolor);
|
||||
}
|
||||
|
|
@ -944,17 +1076,9 @@ void BitmapCanvas::end()
|
|||
window->PresentBitmap(width, height, pixels.data());
|
||||
}
|
||||
|
||||
void BitmapCanvas::begin3d()
|
||||
{
|
||||
}
|
||||
|
||||
void BitmapCanvas::end3d()
|
||||
{
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::unique_ptr<Canvas> Canvas::create(DisplayWindow* window)
|
||||
std::unique_ptr<Canvas> Canvas::create()
|
||||
{
|
||||
return std::make_unique<BitmapCanvas>(window);
|
||||
return std::make_unique<BitmapCanvas>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,9 +156,13 @@ DarkWidgetTheme::DarkWidgetTheme()
|
|||
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));
|
||||
|
|
@ -235,8 +239,18 @@ DarkWidgetTheme::DarkWidgetTheme()
|
|||
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);
|
||||
|
|
@ -266,6 +280,7 @@ LightWidgetTheme::LightWidgetTheme()
|
|||
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");
|
||||
|
|
@ -345,13 +360,19 @@ LightWidgetTheme::LightWidgetTheme()
|
|||
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));
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ TrueTypeFont::TrueTypeFont(std::shared_ptr<TrueTypeFontFileData> initdata, int t
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,17 @@
|
|||
#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)
|
||||
{
|
||||
Widget* owner = parent ? parent->Window() : nullptr;
|
||||
DispWindow = DisplayWindow::Create(this, type == WidgetType::Popup, owner ? owner->DispWindow.get() : nullptr);
|
||||
DispCanvas = Canvas::create(DispWindow.get());
|
||||
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"));
|
||||
|
|
@ -30,6 +34,9 @@ Widget::Widget(Widget* parent, WidgetType type) : Type(type)
|
|||
|
||||
Widget::~Widget()
|
||||
{
|
||||
if (DispCanvas)
|
||||
DispCanvas->detach();
|
||||
|
||||
while (LastChildObj)
|
||||
delete LastChildObj;
|
||||
|
||||
|
|
@ -39,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)
|
||||
|
|
@ -204,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)
|
||||
|
|
@ -307,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)
|
||||
|
|
@ -429,7 +459,7 @@ void Widget::SetCursor(StandardCursor cursor)
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::CaptureMouse()
|
||||
void Widget::SetPointerCapture()
|
||||
{
|
||||
Widget* w = Window();
|
||||
if (w && w->CaptureWidget != this)
|
||||
|
|
@ -439,7 +469,7 @@ void Widget::CaptureMouse()
|
|||
}
|
||||
}
|
||||
|
||||
void Widget::ReleaseMouseCapture()
|
||||
void Widget::ReleasePointerCapture()
|
||||
{
|
||||
Widget* w = Window();
|
||||
if (w && w->CaptureWidget != nullptr)
|
||||
|
|
@ -449,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();
|
||||
|
|
@ -485,6 +533,24 @@ 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())
|
||||
|
|
@ -790,6 +856,18 @@ void* Widget::GetNativeHandle()
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -1,188 +0,0 @@
|
|||
|
||||
#include "systemdialogs/folder_browse_dialog.h"
|
||||
#include "core/widget.h"
|
||||
#include "window/window.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <Shlobj.h>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
class BrowseFolderDialogImpl : public BrowseFolderDialog
|
||||
{
|
||||
public:
|
||||
BrowseFolderDialogImpl(Widget *owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
Widget *owner = nullptr;
|
||||
|
||||
std::string selected_path;
|
||||
std::string initial_directory;
|
||||
std::string title;
|
||||
|
||||
bool Show() override
|
||||
{
|
||||
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 && owner->Window())
|
||||
result = open_dialog->Show((HWND)owner->Window()->GetNativeHandle());
|
||||
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 BrowseFolderDialog::SelectedPath() const override
|
||||
{
|
||||
return selected_path;
|
||||
}
|
||||
|
||||
void BrowseFolderDialog::SetInitialDirectory(const std::string& path) override
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void BrowseFolderDialog::SetTitle(const std::string& newtitle) override
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void throw_if_failed(HRESULT result, const std::string &error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<BrowseFolderDialog> BrowseFolderDialog::Create(Widget* owner)
|
||||
{
|
||||
return std::make_unique<BrowseFolderDialogImpl>(owner);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<BrowseFolderDialog> BrowseFolderDialog::Create(Widget* owner)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,286 +1,12 @@
|
|||
|
||||
#include "systemdialogs/open_file_dialog.h"
|
||||
#include "core/widget.h"
|
||||
#include "window/window.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <Shlobj.h>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
class OpenFileDialogImpl : public OpenFileDialog
|
||||
{
|
||||
public:
|
||||
OpenFileDialogImpl(Widget* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
Widget* 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;
|
||||
|
||||
bool Show() override
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
if (owner && owner->Window())
|
||||
result = open_dialog->Show((HWND)owner->Window()->GetNativeHandle());
|
||||
else
|
||||
result = open_dialog->Show(0);
|
||||
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
ComPtr<IShellItemArray> items;
|
||||
result = open_dialog->GetSelectedItems(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 Filename() const override
|
||||
{
|
||||
return !filenames.empty() ? filenames.front() : std::string();
|
||||
}
|
||||
|
||||
std::vector<std::string> Filenames() const override
|
||||
{
|
||||
return filenames;
|
||||
}
|
||||
|
||||
void SetMultiSelect(bool new_multi_select) override
|
||||
{
|
||||
multi_select = new_multi_select;
|
||||
}
|
||||
|
||||
void SetFilename(const std::string& filename) override
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void ClearFilters() override
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void SetFilterIndex(int filter_index) override
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void SetInitialDirectory(const std::string& path) override
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void SetTitle(const std::string& newtitle) override
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void SetDefaultExtension(const std::string& extension) override
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
|
||||
void throw_if_failed(HRESULT result, const std::string& error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
};
|
||||
#include "core/widget.h"
|
||||
|
||||
std::unique_ptr<OpenFileDialog> OpenFileDialog::Create(Widget* owner)
|
||||
{
|
||||
return std::make_unique<OpenFileDialogImpl>(owner);
|
||||
DisplayWindow* windowOwner = nullptr;
|
||||
if (owner && owner->Window())
|
||||
windowOwner = owner->Window()->DispWindow.get();
|
||||
return DisplayBackend::Get()->CreateOpenFileDialog(windowOwner);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<OpenFileDialog> OpenFileDialog::Create(Widget* owner)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
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);
|
||||
}
|
||||
|
|
@ -1,263 +1,12 @@
|
|||
|
||||
#include "systemdialogs/save_file_dialog.h"
|
||||
#include "core/widget.h"
|
||||
#include "window/window.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <Shlobj.h>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
class SaveFileDialogImpl : public SaveFileDialog
|
||||
{
|
||||
public:
|
||||
SaveFileDialogImpl(Widget* owner) : owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
Widget* 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;
|
||||
|
||||
bool Show() override
|
||||
{
|
||||
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 && owner->Window())
|
||||
result = save_dialog->Show((HWND)owner->Window()->GetNativeHandle());
|
||||
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 Filename() const override
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
void SetFilename(const std::string& filename) override
|
||||
{
|
||||
initial_filename = filename;
|
||||
}
|
||||
|
||||
void AddFilter(const std::string& filter_description, const std::string& filter_extension) override
|
||||
{
|
||||
Filter f;
|
||||
f.description = filter_description;
|
||||
f.extension = filter_extension;
|
||||
filters.push_back(std::move(f));
|
||||
}
|
||||
|
||||
void ClearFilters() override
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void SetFilterIndex(int filter_index) override
|
||||
{
|
||||
filterindex = filter_index;
|
||||
}
|
||||
|
||||
void SetInitialDirectory(const std::string& path) override
|
||||
{
|
||||
initial_directory = path;
|
||||
}
|
||||
|
||||
void SetTitle(const std::string& newtitle) override
|
||||
{
|
||||
title = newtitle;
|
||||
}
|
||||
|
||||
void SetDefaultExtension(const std::string& extension) override
|
||||
{
|
||||
defaultext = extension;
|
||||
}
|
||||
|
||||
void throw_if_failed(HRESULT result, const std::string& error)
|
||||
{
|
||||
if (FAILED(result))
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
};
|
||||
#include "core/widget.h"
|
||||
|
||||
std::unique_ptr<SaveFileDialog> SaveFileDialog::Create(Widget* owner)
|
||||
{
|
||||
return std::make_unique<SaveFileDialogImpl>(owner);
|
||||
DisplayWindow* windowOwner = nullptr;
|
||||
if (owner && owner->Window())
|
||||
windowOwner = owner->Window()->DispWindow.get();
|
||||
return DisplayBackend::Get()->CreateSaveFileDialog(windowOwner);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<SaveFileDialog> SaveFileDialog::Create(Widget* owner)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -30,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,4 +1,4 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "widgets/lineedit/lineedit.h"
|
||||
#include "core/utf8reader.h"
|
||||
#include "core/colorf.h"
|
||||
|
|
@ -19,8 +19,6 @@ LineEdit::LineEdit(Widget* parent) : Widget(parent)
|
|||
|
||||
LineEdit::~LineEdit()
|
||||
{
|
||||
delete timer;
|
||||
delete scroll_timer;
|
||||
}
|
||||
|
||||
bool LineEdit::IsReadOnly() const
|
||||
|
|
@ -310,7 +308,7 @@ bool LineEdit::OnMouseDown(const Point& pos, InputKey key)
|
|||
{
|
||||
if (HasFocus())
|
||||
{
|
||||
CaptureMouse();
|
||||
SetPointerCapture();
|
||||
mouse_selecting = true;
|
||||
cursor_pos = GetCharacterIndex(pos.x);
|
||||
SetTextSelection(cursor_pos, 0);
|
||||
|
|
@ -335,14 +333,14 @@ bool LineEdit::OnMouseUp(const Point& pos, InputKey key)
|
|||
{
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,19 @@ void ListView::AddItem(const std::string& text)
|
|||
Update();
|
||||
}
|
||||
|
||||
void ListView::RemoveItem(int index)
|
||||
{
|
||||
if (index >= 0 && index < items.size())
|
||||
{
|
||||
items.erase(items.begin() + index);
|
||||
}
|
||||
|
||||
if (selectedItem == items.size())
|
||||
{
|
||||
selectedItem = !items.empty() ? (int)items.size() - 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ListView::Activate()
|
||||
{
|
||||
if (OnActivated)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,17 @@ MenubarItem* Menubar::AddItem(std::string text, std::function<void(Menu* menu)>
|
|||
|
||||
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);
|
||||
|
|
@ -38,8 +48,13 @@ void Menubar::ShowMenu(MenubarItem* item)
|
|||
|
||||
void Menubar::CloseMenu()
|
||||
{
|
||||
//delete openMenu;
|
||||
//openMenu = nullptr;
|
||||
if (currentMenubarItem != -1)
|
||||
menuItems[currentMenubarItem]->SetStyleState("");
|
||||
currentMenubarItem = -1;
|
||||
delete openMenu;
|
||||
openMenu = nullptr;
|
||||
ReleaseModalCapture();
|
||||
modalMode = false;
|
||||
}
|
||||
|
||||
void Menubar::OnGeometryChanged()
|
||||
|
|
@ -65,6 +80,139 @@ void Menubar::OnGeometryChanged()
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -127,20 +275,10 @@ void Menu::SetRightPosition(const Point& pos)
|
|||
|
||||
MenuItem* Menu::AddItem(std::shared_ptr<Image> icon, std::string text, std::function<void()> onClick)
|
||||
{
|
||||
auto item = new MenuItem(this);
|
||||
auto item = new MenuItem(this, [this, onClick]() { if (onCloseMenu) onCloseMenu(); if (onClick) onClick(); });
|
||||
if (icon)
|
||||
item->icon->SetImage(icon);
|
||||
item->text->SetText(text);
|
||||
/*
|
||||
item->element->addEventListener("click", [=](Event* event)
|
||||
{
|
||||
event->stopPropagation();
|
||||
if (onCloseMenu)
|
||||
onCloseMenu();
|
||||
if (onClick)
|
||||
onClick();
|
||||
});
|
||||
*/
|
||||
return item;
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +290,7 @@ MenuItemSeparator* Menu::AddSeparator()
|
|||
|
||||
double Menu::GetPreferredWidth() const
|
||||
{
|
||||
return 200.0;
|
||||
return GridFitSize(200.0);
|
||||
}
|
||||
|
||||
double Menu::GetPreferredHeight() const
|
||||
|
|
@ -160,7 +298,8 @@ double Menu::GetPreferredHeight() const
|
|||
double h = 0.0;
|
||||
for (Widget* item = FirstChild(); item != nullptr; item = item->NextSibling())
|
||||
{
|
||||
h += 20.0;
|
||||
double itemheight = GridFitSize((item->GetStyleClass() == "menuitemseparator") ? 7.0 : 30.0);
|
||||
h += itemheight;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
|
@ -168,18 +307,31 @@ double Menu::GetPreferredHeight() const
|
|||
void Menu::OnGeometryChanged()
|
||||
{
|
||||
double w = GetWidth();
|
||||
double h = GetHeight();
|
||||
double y = 0.0;
|
||||
for (Widget* item = FirstChild(); item != nullptr; item = item->NextSibling())
|
||||
{
|
||||
item->SetFrameGeometry(Rect::xywh(0.0, y, w, 20.0));
|
||||
y += 20.0;
|
||||
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(Widget* parent) : Widget(parent)
|
||||
MenuItem::MenuItem(Menu* menu, std::function<void()> onClick) : Widget(menu), menu(menu), onClick(onClick)
|
||||
{
|
||||
SetStyleClass("menuitem");
|
||||
icon = new ImageBox(this);
|
||||
|
|
@ -188,25 +340,41 @@ MenuItem::MenuItem(Widget* parent) : Widget(parent)
|
|||
|
||||
void MenuItem::OnMouseMove(const Point& pos)
|
||||
{
|
||||
if (GetStyleState().empty())
|
||||
menu->SetSelected(this);
|
||||
}
|
||||
|
||||
bool MenuItem::OnMouseUp(const Point& pos, InputKey key)
|
||||
{
|
||||
Click();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MenuItem::Click()
|
||||
{
|
||||
if (onClick)
|
||||
{
|
||||
SetStyleState("hover");
|
||||
// We have to make a copy of the handler as it may delete 'this'
|
||||
auto handler = onClick;
|
||||
handler();
|
||||
}
|
||||
}
|
||||
|
||||
void MenuItem::OnMouseLeave()
|
||||
{
|
||||
SetStyleState("");
|
||||
menu->SetSelected(nullptr);
|
||||
}
|
||||
|
||||
void MenuItem::OnGeometryChanged()
|
||||
{
|
||||
double iconwidth = icon->GetPreferredWidth();
|
||||
double iconheight = icon->GetPreferredHeight();
|
||||
double iconwidth = GridFitSize(icon->GetPreferredWidth());
|
||||
double iconheight = GridFitSize(icon->GetPreferredHeight());
|
||||
double w = GetWidth();
|
||||
double h = GetHeight();
|
||||
icon->SetFrameGeometry(Rect::xywh(0.0, (h - iconheight) * 0.5, iconwidth, iconheight));
|
||||
text->SetFrameGeometry(Rect::xywh(iconwidth, 0.0, w - iconwidth, h));
|
||||
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));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -215,3 +383,8 @@ 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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ bool Scrollbar::OnMouseDown(const Point& pos, InputKey key)
|
|||
UpdatePartPositions();
|
||||
|
||||
Update();
|
||||
CaptureMouse();
|
||||
SetPointerCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ bool Scrollbar::OnMouseUp(const Point& pos, InputKey key)
|
|||
mouse_down_timer->Stop();
|
||||
|
||||
Update();
|
||||
ReleaseMouseCapture();
|
||||
ReleasePointerCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -290,7 +291,7 @@ bool TextEdit::OnMouseDown(const Point& pos, InputKey key)
|
|||
{
|
||||
if (key == InputKey::LeftMouse)
|
||||
{
|
||||
CaptureMouse();
|
||||
SetPointerCapture();
|
||||
mouse_selecting = true;
|
||||
cursor_pos = GetCharacterIndex(pos);
|
||||
selection_start = cursor_pos;
|
||||
|
|
@ -312,14 +313,14 @@ bool TextEdit::OnMouseUp(const Point& pos, InputKey key)
|
|||
{
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
};
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#include "sdl2displaywindow.h"
|
||||
#include "sdl2_display_window.h"
|
||||
#include <stdexcept>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
Uint32 SDL2DisplayWindow::PaintEventNumber = 0xffffffff;
|
||||
bool SDL2DisplayWindow::ExitRunLoop;
|
||||
|
|
@ -24,23 +25,43 @@ static void CheckInitSDL()
|
|||
static InitSDL initsdl;
|
||||
}
|
||||
|
||||
SDL2DisplayWindow::SDL2DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, SDL2DisplayWindow* owner) : WindowHost(windowHost)
|
||||
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;
|
||||
int result = SDL_CreateWindowAndRenderer(320, 200, flags, &WindowHandle, &RendererHandle);
|
||||
if (result != 0)
|
||||
throw std::runtime_error(std::string("Unable to create SDL window:") + SDL_GetError());
|
||||
|
||||
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(WindowHandle)] = this;
|
||||
WindowList[SDL_GetWindowID(Handle.window)] = this;
|
||||
}
|
||||
|
||||
SDL2DisplayWindow::~SDL2DisplayWindow()
|
||||
{
|
||||
WindowList.erase(WindowList.find(SDL_GetWindowID(WindowHandle)));
|
||||
UnlockCursor();
|
||||
|
||||
WindowList.erase(WindowList.find(SDL_GetWindowID(Handle.window)));
|
||||
|
||||
if (BackBufferTexture)
|
||||
{
|
||||
|
|
@ -48,15 +69,39 @@ SDL2DisplayWindow::~SDL2DisplayWindow()
|
|||
BackBufferTexture = nullptr;
|
||||
}
|
||||
|
||||
SDL_DestroyRenderer(RendererHandle);
|
||||
SDL_DestroyWindow(WindowHandle);
|
||||
if (RendererHandle)
|
||||
SDL_DestroyRenderer(RendererHandle);
|
||||
SDL_DestroyWindow(Handle.window);
|
||||
RendererHandle = nullptr;
|
||||
WindowHandle = 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(WindowHandle, text.c_str());
|
||||
SDL_SetWindowTitle(Handle.window, text.c_str());
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::SetWindowFrame(const Rect& box)
|
||||
|
|
@ -76,46 +121,54 @@ void SDL2DisplayWindow::SetClientFrame(const Rect& box)
|
|||
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);
|
||||
SDL_SetWindowPosition(Handle.window, x, y);
|
||||
SDL_SetWindowSize(Handle.window, w, h);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Show()
|
||||
{
|
||||
SDL_ShowWindow(WindowHandle);
|
||||
SDL_ShowWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowFullscreen()
|
||||
{
|
||||
SDL_SetWindowFullscreen(WindowHandle, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_SetWindowFullscreen(Handle.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
isFullscreen = true;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowMaximized()
|
||||
{
|
||||
SDL_ShowWindow(WindowHandle);
|
||||
SDL_MaximizeWindow(WindowHandle);
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_MaximizeWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowMinimized()
|
||||
{
|
||||
SDL_ShowWindow(WindowHandle);
|
||||
SDL_MinimizeWindow(WindowHandle);
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_MinimizeWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowNormal()
|
||||
{
|
||||
SDL_ShowWindow(WindowHandle);
|
||||
SDL_SetWindowFullscreen(WindowHandle, 0);
|
||||
SDL_ShowWindow(Handle.window);
|
||||
SDL_SetWindowFullscreen(Handle.window, 0);
|
||||
isFullscreen = false;
|
||||
}
|
||||
|
||||
bool SDL2DisplayWindow::IsWindowFullscreen()
|
||||
{
|
||||
return isFullscreen;
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Hide()
|
||||
{
|
||||
SDL_HideWindow(WindowHandle);
|
||||
SDL_HideWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::Activate()
|
||||
{
|
||||
SDL_RaiseWindow(WindowHandle);
|
||||
SDL_RaiseWindow(Handle.window);
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::ShowCursor(bool enable)
|
||||
|
|
@ -125,14 +178,20 @@ void SDL2DisplayWindow::ShowCursor(bool enable)
|
|||
|
||||
void SDL2DisplayWindow::LockCursor()
|
||||
{
|
||||
SDL_SetWindowGrab(WindowHandle, SDL_TRUE);
|
||||
SDL_ShowCursor(0);
|
||||
if (!CursorLocked)
|
||||
{
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
CursorLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::UnlockCursor()
|
||||
{
|
||||
SDL_SetWindowGrab(WindowHandle, SDL_FALSE);
|
||||
SDL_ShowCursor(1);
|
||||
if (CursorLocked)
|
||||
{
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
CursorLocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::CaptureMouse()
|
||||
|
|
@ -151,7 +210,7 @@ void SDL2DisplayWindow::Update()
|
|||
{
|
||||
SDL_Event event = {};
|
||||
event.type = PaintEventNumber;
|
||||
event.user.windowID = SDL_GetWindowID(WindowHandle);
|
||||
event.user.windowID = SDL_GetWindowID(Handle.window);
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
|
|
@ -172,8 +231,8 @@ Rect SDL2DisplayWindow::GetWindowFrame() const
|
|||
int w = 0;
|
||||
int h = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowPosition(WindowHandle, &x, &y);
|
||||
SDL_GetWindowSize(WindowHandle, &w, &h);
|
||||
SDL_GetWindowPosition(Handle.window, &x, &y);
|
||||
SDL_GetWindowSize(Handle.window, &w, &h);
|
||||
return Rect::xywh(x / uiscale, y / uiscale, w / uiscale, h / uiscale);
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +241,7 @@ Point SDL2DisplayWindow::MapFromGlobal(const Point& pos) const
|
|||
int x = 0;
|
||||
int y = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowPosition(WindowHandle, &x, &y);
|
||||
SDL_GetWindowPosition(Handle.window, &x, &y);
|
||||
return Point(pos.x - x / uiscale, pos.y - y / uiscale);
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +250,7 @@ Point SDL2DisplayWindow::MapToGlobal(const Point& pos) const
|
|||
int x = 0;
|
||||
int y = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowPosition(WindowHandle, &x, &y);
|
||||
SDL_GetWindowPosition(Handle.window, &x, &y);
|
||||
return Point(pos.x + x / uiscale, pos.y + y / uiscale);
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +259,7 @@ Size SDL2DisplayWindow::GetClientSize() const
|
|||
int w = 0;
|
||||
int h = 0;
|
||||
double uiscale = GetDpiScale();
|
||||
SDL_GetWindowSize(WindowHandle, &w, &h);
|
||||
SDL_GetWindowSize(Handle.window, &w, &h);
|
||||
return Size(w / uiscale, h / uiscale);
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +267,10 @@ int SDL2DisplayWindow::GetPixelWidth() const
|
|||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int result = SDL_GetRendererOutputSize(RendererHandle, &w, &h);
|
||||
if (RendererHandle)
|
||||
SDL_GetRendererOutputSize(RendererHandle, &w, &h);
|
||||
else
|
||||
SDL_GL_GetDrawableSize(Handle.window, &w, &h);
|
||||
return w;
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +278,10 @@ int SDL2DisplayWindow::GetPixelHeight() const
|
|||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int result = SDL_GetRendererOutputSize(RendererHandle, &w, &h);
|
||||
if (RendererHandle)
|
||||
SDL_GetRendererOutputSize(RendererHandle, &w, &h);
|
||||
else
|
||||
SDL_GL_GetDrawableSize(Handle.window, &w, &h);
|
||||
return h;
|
||||
}
|
||||
|
||||
|
|
@ -228,6 +293,9 @@ double SDL2DisplayWindow::GetDpiScale() const
|
|||
|
||||
void SDL2DisplayWindow::PresentBitmap(int width, int height, const uint32_t* pixels)
|
||||
{
|
||||
if (!RendererHandle)
|
||||
return;
|
||||
|
||||
if (!BackBufferTexture || BackBufferWidth != width || BackBufferHeight != height)
|
||||
{
|
||||
if (BackBufferTexture)
|
||||
|
|
@ -305,6 +373,7 @@ void SDL2DisplayWindow::RunLoop()
|
|||
{
|
||||
CheckInitSDL();
|
||||
|
||||
ExitRunLoop = false;
|
||||
while (!ExitRunLoop)
|
||||
{
|
||||
SDL_Event event = {};
|
||||
|
|
@ -463,7 +532,14 @@ void SDL2DisplayWindow::OnMouseWheel(const SDL_MouseWheelEvent& event)
|
|||
|
||||
void SDL2DisplayWindow::OnMouseMotion(const SDL_MouseMotionEvent& event)
|
||||
{
|
||||
WindowHost->OnWindowMouseMove(GetMousePos(event));
|
||||
if (CursorLocked)
|
||||
{
|
||||
WindowHost->OnWindowRawMouseMove(event.xrel, event.yrel);
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowHost->OnWindowMouseMove(GetMousePos(event));
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2DisplayWindow::OnPaintEvent()
|
||||
|
|
@ -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, bool popupWindow, SDL2DisplayWindow* owner);
|
||||
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;
|
||||
|
|
@ -48,7 +50,10 @@ public:
|
|||
Point MapFromGlobal(const Point& pos) const override;
|
||||
Point MapToGlobal(const Point& pos) const override;
|
||||
|
||||
void* GetNativeHandle() override { return WindowHandle; }
|
||||
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);
|
||||
|
|
@ -84,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;
|
||||
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; }
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#include "win32displaywindow.h"
|
||||
#include "win32_display_window.h"
|
||||
#include <windowsx.h>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
|
|
@ -28,40 +28,12 @@
|
|||
#define RIDEV_INPUTSINK (0x100)
|
||||
#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;
|
||||
}
|
||||
|
||||
Win32DisplayWindow::Win32DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, Win32DisplayWindow* owner) : WindowHost(windowHost)
|
||||
Win32DisplayWindow::Win32DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, Win32DisplayWindow* owner, RenderAPI renderAPI) : WindowHost(windowHost), PopupWindow(popupWindow)
|
||||
{
|
||||
Windows.push_front(this);
|
||||
WindowsIterator = Windows.begin();
|
||||
|
||||
WNDCLASSEXW classdesc = {};
|
||||
WNDCLASSEX classdesc = {};
|
||||
classdesc.cbSize = sizeof(WNDCLASSEX);
|
||||
classdesc.hInstance = GetModuleHandle(0);
|
||||
classdesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
|
||||
|
|
@ -78,6 +50,7 @@ Win32DisplayWindow::Win32DisplayWindow(DisplayWindowHost* windowHost, bool popup
|
|||
DWORD style = 0, exstyle = 0;
|
||||
if (popupWindow)
|
||||
{
|
||||
exstyle = WS_EX_NOACTIVATE;
|
||||
style = WS_POPUP;
|
||||
}
|
||||
else
|
||||
|
|
@ -85,24 +58,15 @@ Win32DisplayWindow::Win32DisplayWindow(DisplayWindowHost* windowHost, bool popup
|
|||
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 : 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));
|
||||
*/
|
||||
CreateWindowEx(exstyle, L"ZWidgetWindow", L"", style, 0, 0, 100, 100, owner ? owner->WindowHandle.hwnd : 0, 0, GetModuleHandle(0), this);
|
||||
}
|
||||
|
||||
Win32DisplayWindow::~Win32DisplayWindow()
|
||||
{
|
||||
if (WindowHandle)
|
||||
if (WindowHandle.hwnd)
|
||||
{
|
||||
DestroyWindow(WindowHandle);
|
||||
WindowHandle = 0;
|
||||
DestroyWindow(WindowHandle.hwnd);
|
||||
WindowHandle.hwnd = 0;
|
||||
}
|
||||
|
||||
Windows.erase(WindowsIterator);
|
||||
|
|
@ -110,31 +74,31 @@ Win32DisplayWindow::~Win32DisplayWindow()
|
|||
|
||||
void Win32DisplayWindow::SetWindowTitle(const std::string& text)
|
||||
{
|
||||
SetWindowText(WindowHandle, to_utf16(text).c_str());
|
||||
SetWindowText(WindowHandle.hwnd, to_utf16(text).c_str());
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetBorderColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle, 34/*DWMWA_BORDER_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
DwmSetWindowAttribute(WindowHandle.hwnd, 34/*DWMWA_BORDER_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetCaptionColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle, 35/*DWMWA_CAPTION_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
DwmSetWindowAttribute(WindowHandle.hwnd, 35/*DWMWA_CAPTION_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::SetCaptionTextColor(uint32_t bgra8)
|
||||
{
|
||||
bgra8 = bgra8 & 0x00ffffff;
|
||||
DwmSetWindowAttribute(WindowHandle, 36/*DWMWA_TEXT_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
DwmSetWindowAttribute(WindowHandle.hwnd, 36/*DWMWA_TEXT_COLOR*/, &bgra8, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::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);
|
||||
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)
|
||||
|
|
@ -147,16 +111,16 @@ void Win32DisplayWindow::SetClientFrame(const Rect& box)
|
|||
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));
|
||||
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, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER);
|
||||
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, SW_SHOW);
|
||||
ShowWindow(WindowHandle.hwnd, PopupWindow ? SW_SHOWNA : SW_SHOW);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowFullscreen()
|
||||
|
|
@ -165,35 +129,47 @@ void Win32DisplayWindow::ShowFullscreen()
|
|||
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);
|
||||
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, SW_SHOWMAXIMIZED);
|
||||
ShowWindow(WindowHandle.hwnd, SW_SHOWMAXIMIZED);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowMinimized()
|
||||
{
|
||||
ShowWindow(WindowHandle, SW_SHOWMINIMIZED);
|
||||
ShowWindow(WindowHandle.hwnd, SW_SHOWMINIMIZED);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowNormal()
|
||||
{
|
||||
ShowWindow(WindowHandle, SW_NORMAL);
|
||||
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, SW_HIDE);
|
||||
ShowWindow(WindowHandle.hwnd, SW_HIDE);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::Activate()
|
||||
{
|
||||
SetFocus(WindowHandle);
|
||||
if (!PopupWindow)
|
||||
SetFocus(WindowHandle.hwnd);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ShowCursor(bool enable)
|
||||
|
|
@ -207,6 +183,13 @@ void Win32DisplayWindow::LockCursor()
|
|||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,6 +197,13 @@ 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);
|
||||
|
|
@ -222,7 +212,7 @@ void Win32DisplayWindow::UnlockCursor()
|
|||
|
||||
void Win32DisplayWindow::CaptureMouse()
|
||||
{
|
||||
SetCapture(WindowHandle);
|
||||
SetCapture(WindowHandle.hwnd);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::ReleaseMouseCapture()
|
||||
|
|
@ -232,7 +222,7 @@ void Win32DisplayWindow::ReleaseMouseCapture()
|
|||
|
||||
void Win32DisplayWindow::Update()
|
||||
{
|
||||
InvalidateRect(WindowHandle, nullptr, FALSE);
|
||||
InvalidateRect(WindowHandle.hwnd, nullptr, FALSE);
|
||||
}
|
||||
|
||||
bool Win32DisplayWindow::GetKeyState(InputKey key)
|
||||
|
|
@ -252,9 +242,9 @@ void Win32DisplayWindow::SetCursor(StandardCursor cursor)
|
|||
Rect Win32DisplayWindow::GetWindowFrame() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetWindowRect(WindowHandle, &box);
|
||||
GetWindowRect(WindowHandle.hwnd, &box);
|
||||
double dpiscale = GetDpiScale();
|
||||
return Rect(box.left / dpiscale, box.top / dpiscale, box.right / dpiscale, box.bottom / dpiscale);
|
||||
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
|
||||
|
|
@ -263,7 +253,7 @@ Point Win32DisplayWindow::MapFromGlobal(const Point& pos) const
|
|||
POINT point = {};
|
||||
point.x = (LONG)std::round(pos.x / dpiscale);
|
||||
point.y = (LONG)std::round(pos.y / dpiscale);
|
||||
ScreenToClient(WindowHandle, &point);
|
||||
ScreenToClient(WindowHandle.hwnd, &point);
|
||||
return Point(point.x * dpiscale, point.y * dpiscale);
|
||||
}
|
||||
|
||||
|
|
@ -273,14 +263,14 @@ Point Win32DisplayWindow::MapToGlobal(const Point& pos) const
|
|||
POINT point = {};
|
||||
point.x = (LONG)std::round(pos.x * dpiscale);
|
||||
point.y = (LONG)std::round(pos.y * dpiscale);
|
||||
ClientToScreen(WindowHandle, &point);
|
||||
ClientToScreen(WindowHandle.hwnd, &point);
|
||||
return Point(point.x / dpiscale, point.y / dpiscale);
|
||||
}
|
||||
|
||||
Size Win32DisplayWindow::GetClientSize() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle, &box);
|
||||
GetClientRect(WindowHandle.hwnd, &box);
|
||||
double dpiscale = GetDpiScale();
|
||||
return Size(box.right / dpiscale, box.bottom / dpiscale);
|
||||
}
|
||||
|
|
@ -288,25 +278,25 @@ Size Win32DisplayWindow::GetClientSize() const
|
|||
int Win32DisplayWindow::GetPixelWidth() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle, &box);
|
||||
GetClientRect(WindowHandle.hwnd, &box);
|
||||
return box.right;
|
||||
}
|
||||
|
||||
int Win32DisplayWindow::GetPixelHeight() const
|
||||
{
|
||||
RECT box = {};
|
||||
GetClientRect(WindowHandle, &box);
|
||||
GetClientRect(WindowHandle.hwnd, &box);
|
||||
return box.bottom;
|
||||
}
|
||||
|
||||
double Win32DisplayWindow::GetDpiScale() const
|
||||
{
|
||||
return GetDpiForWindow(WindowHandle) / 96.0;
|
||||
return GetDpiForWindow(WindowHandle.hwnd) / 96.0;
|
||||
}
|
||||
|
||||
std::string Win32DisplayWindow::GetClipboardText()
|
||||
{
|
||||
BOOL result = OpenClipboard(WindowHandle);
|
||||
BOOL result = OpenClipboard(WindowHandle.hwnd);
|
||||
if (result == FALSE)
|
||||
throw std::runtime_error("Unable to open clipboard");
|
||||
|
||||
|
|
@ -334,7 +324,7 @@ void Win32DisplayWindow::SetClipboardText(const std::string& text)
|
|||
{
|
||||
std::wstring text16 = to_utf16(text);
|
||||
|
||||
BOOL result = OpenClipboard(WindowHandle);
|
||||
BOOL result = OpenClipboard(WindowHandle.hwnd);
|
||||
if (result == FALSE)
|
||||
throw std::runtime_error("Unable to open clipboard");
|
||||
|
||||
|
|
@ -395,14 +385,14 @@ void Win32DisplayWindow::PresentBitmap(int width, int height, const uint32_t* pi
|
|||
if (dc != 0)
|
||||
{
|
||||
int result = SetDIBitsToDevice(dc, 0, 0, width, height, 0, 0, 0, height, pixels, (const BITMAPINFO*)&header, BI_RGB);
|
||||
ReleaseDC(WindowHandle, dc);
|
||||
ReleaseDC(WindowHandle.hwnd, dc);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT Win32DisplayWindow::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
LPARAM result = 0;
|
||||
if (DwmDefWindowProc(WindowHandle, msg, wparam, lparam, &result))
|
||||
if (DwmDefWindowProc(WindowHandle.hwnd, msg, wparam, lparam, &result))
|
||||
return result;
|
||||
|
||||
if (msg == WM_INPUT)
|
||||
|
|
@ -427,16 +417,16 @@ LRESULT Win32DisplayWindow::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lpar
|
|||
}
|
||||
}
|
||||
}
|
||||
return DefWindowProc(WindowHandle, msg, wparam, lparam);
|
||||
return DefWindowProc(WindowHandle.hwnd, msg, wparam, lparam);
|
||||
}
|
||||
else if (msg == WM_PAINT)
|
||||
{
|
||||
PAINTSTRUCT paintStruct = {};
|
||||
PaintDC = BeginPaint(WindowHandle, &paintStruct);
|
||||
PaintDC = BeginPaint(WindowHandle.hwnd, &paintStruct);
|
||||
if (PaintDC)
|
||||
{
|
||||
WindowHost->OnWindowPaint();
|
||||
EndPaint(WindowHandle, &paintStruct);
|
||||
EndPaint(WindowHandle.hwnd, &paintStruct);
|
||||
PaintDC = 0;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -445,17 +435,23 @@ LRESULT Win32DisplayWindow::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lpar
|
|||
{
|
||||
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, &box);
|
||||
GetClientRect(WindowHandle.hwnd, &box);
|
||||
|
||||
POINT center = {};
|
||||
center.x = box.right / 2;
|
||||
center.y = box.bottom / 2;
|
||||
ClientToScreen(WindowHandle, ¢er);
|
||||
ClientToScreen(WindowHandle.hwnd, ¢er);
|
||||
|
||||
SetCursorPos(center.x, center.y);
|
||||
}
|
||||
|
|
@ -468,7 +464,7 @@ LRESULT Win32DisplayWindow::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lpar
|
|||
{
|
||||
TRACKMOUSEEVENT eventTrack = {};
|
||||
eventTrack.cbSize = sizeof(TRACKMOUSEEVENT);
|
||||
eventTrack.hwndTrack = WindowHandle;
|
||||
eventTrack.hwndTrack = WindowHandle.hwnd;
|
||||
eventTrack.dwFlags = TME_LEAVE;
|
||||
if (TrackMouseEvent(&eventTrack))
|
||||
TrackMouseActive = true;
|
||||
|
|
@ -526,7 +522,7 @@ LRESULT Win32DisplayWindow::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lpar
|
|||
POINT pos;
|
||||
pos.x = GET_X_LPARAM(lparam);
|
||||
pos.y = GET_Y_LPARAM(lparam);
|
||||
ScreenToClient(WindowHandle, &pos);
|
||||
ScreenToClient(WindowHandle.hwnd, &pos);
|
||||
|
||||
WindowHost->OnWindowMouseWheel(Point(pos.x / dpiscale, pos.y / dpiscale), delta < 0.0 ? InputKey::MouseWheelDown : InputKey::MouseWheelUp);
|
||||
}
|
||||
|
|
@ -573,7 +569,7 @@ LRESULT Win32DisplayWindow::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lpar
|
|||
return WVR_REDRAW;
|
||||
}*/
|
||||
|
||||
return DefWindowProc(WindowHandle, msg, wparam, lparam);
|
||||
return DefWindowProc(WindowHandle.hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
void Win32DisplayWindow::UpdateCursor()
|
||||
|
|
@ -612,7 +608,7 @@ LRESULT Win32DisplayWindow::WndProc(HWND windowhandle, UINT msg, WPARAM wparam,
|
|||
{
|
||||
CREATESTRUCT* createstruct = (CREATESTRUCT*)lparam;
|
||||
Win32DisplayWindow* viewport = (Win32DisplayWindow*)createstruct->lpCreateParams;
|
||||
viewport->WindowHandle = windowhandle;
|
||||
viewport->WindowHandle.hwnd = windowhandle;
|
||||
SetWindowLongPtr(windowhandle, GWLP_USERDATA, (LONG_PTR)viewport);
|
||||
return viewport->OnWindowMessage(msg, wparam, lparam);
|
||||
}
|
||||
|
|
@ -625,7 +621,7 @@ LRESULT Win32DisplayWindow::WndProc(HWND windowhandle, UINT msg, WPARAM wparam,
|
|||
if (msg == WM_DESTROY)
|
||||
{
|
||||
SetWindowLongPtr(windowhandle, GWLP_USERDATA, 0);
|
||||
viewport->WindowHandle = 0;
|
||||
viewport->WindowHandle.hwnd = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -677,6 +673,87 @@ Size Win32DisplayWindow::GetScreenSize()
|
|||
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);
|
||||
|
|
@ -1,20 +1,16 @@
|
|||
#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 Win32DisplayWindow : public DisplayWindow
|
||||
{
|
||||
public:
|
||||
Win32DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, Win32DisplayWindow* owner);
|
||||
Win32DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, Win32DisplayWindow* owner, RenderAPI renderAPI);
|
||||
~Win32DisplayWindow();
|
||||
|
||||
void SetWindowTitle(const std::string& text) 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;
|
||||
|
|
@ -58,7 +55,10 @@ public:
|
|||
|
||||
Point GetLParamPos(LPARAM lparam) const;
|
||||
|
||||
void* GetNativeHandle() override { return reinterpret_cast<void*>(WindowHandle); }
|
||||
void* GetNativeHandle() override { return &WindowHandle; }
|
||||
|
||||
std::vector<std::string> GetVulkanInstanceExtensions() override;
|
||||
VkSurfaceKHR CreateVulkanSurface(VkInstance instance) override;
|
||||
|
||||
static void ProcessEvents();
|
||||
static void RunLoop();
|
||||
|
|
@ -78,8 +78,9 @@ 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;
|
||||
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,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/win32displaywindow.h"
|
||||
|
||||
std::unique_ptr<DisplayWindow> DisplayWindow::Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner)
|
||||
std::unique_ptr<DisplayWindow> DisplayWindow::Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI)
|
||||
{
|
||||
return std::make_unique<Win32DisplayWindow>(windowHost, popupWindow, static_cast<Win32DisplayWindow*>(owner));
|
||||
return DisplayBackend::Get()->Create(windowHost, popupWindow, owner, renderAPI);
|
||||
}
|
||||
|
||||
void DisplayWindow::ProcessEvents()
|
||||
{
|
||||
Win32DisplayWindow::ProcessEvents();
|
||||
DisplayBackend::Get()->ProcessEvents();
|
||||
}
|
||||
|
||||
void DisplayWindow::RunLoop()
|
||||
{
|
||||
Win32DisplayWindow::RunLoop();
|
||||
DisplayBackend::Get()->RunLoop();
|
||||
}
|
||||
|
||||
void DisplayWindow::ExitLoop()
|
||||
{
|
||||
Win32DisplayWindow::ExitLoop();
|
||||
}
|
||||
|
||||
Size DisplayWindow::GetScreenSize()
|
||||
{
|
||||
return Win32DisplayWindow::GetScreenSize();
|
||||
DisplayBackend::Get()->ExitLoop();
|
||||
}
|
||||
|
||||
void* DisplayWindow::StartTimer(int timeoutMilliseconds, std::function<void()> onTimer)
|
||||
{
|
||||
return Win32DisplayWindow::StartTimer(timeoutMilliseconds, std::move(onTimer));
|
||||
return DisplayBackend::Get()->StartTimer(timeoutMilliseconds, onTimer);
|
||||
}
|
||||
|
||||
void DisplayWindow::StopTimer(void* timerID)
|
||||
{
|
||||
Win32DisplayWindow::StopTimer(timerID);
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
std::unique_ptr<DisplayWindow> DisplayWindow::Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner)
|
||||
{
|
||||
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, bool popupWindow, DisplayWindow* owner)
|
||||
std::unique_ptr<DisplayBackend> DisplayBackend::TryCreateWin32()
|
||||
{
|
||||
return std::make_unique<SDL2DisplayWindow>(windowHost, popupWindow, static_cast<SDL2DisplayWindow*>(owner));
|
||||
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
|
||||
};
|
||||
1161
libraries/ZWidget/src/window/x11/x11_display_window.cpp
Normal file
1161
libraries/ZWidget/src/window/x11/x11_display_window.cpp
Normal file
File diff suppressed because it is too large
Load diff
137
libraries/ZWidget/src/window/x11/x11_display_window.h
Normal file
137
libraries/ZWidget/src/window/x11/x11_display_window.h
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#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;
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
@ -3901,6 +3901,15 @@ int D_DoomMain_Game()
|
|||
|
||||
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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue