├── winio-ui-qt ├── src │ ├── runtime.rs │ ├── ui │ │ ├── monitor.hpp │ │ ├── label.hpp │ │ ├── progress.hpp │ │ ├── scroll_view.hpp │ │ ├── monitor.cpp │ │ ├── list_box.hpp │ │ ├── tab_view.hpp │ │ ├── widget.cpp │ │ ├── tab_view.cpp │ │ ├── widget.hpp │ │ ├── list_box.cpp │ │ ├── msgbox.hpp │ │ ├── filebox.hpp │ │ ├── combo_box.hpp │ │ ├── webview.hpp │ │ ├── edit.hpp │ │ ├── scroll_bar.hpp │ │ ├── combo_box.cpp │ │ ├── msgbox.cpp │ │ ├── button.cpp │ │ ├── webview.cpp │ │ ├── edit.cpp │ │ ├── button.hpp │ │ ├── filebox.cpp │ │ ├── scroll_bar.cpp │ │ ├── monitor.rs │ │ ├── media.cpp │ │ ├── window.hpp │ │ ├── media.hpp │ │ ├── common.hpp │ │ └── window.cpp │ ├── runtime │ │ ├── qt.hpp │ │ ├── qt.cpp │ │ └── qt.rs │ └── lib.rs ├── Cargo.toml └── build.rs ├── winio-ui-winui ├── src │ ├── hook │ │ ├── mod.rs │ │ ├── mq.rs │ │ └── mrm.rs │ ├── lib.rs │ └── ui │ │ ├── label.rs │ │ ├── button.rs │ │ ├── progress.rs │ │ ├── check_box.rs │ │ └── radio_button.rs └── Cargo.toml ├── assets ├── qt.dark.png ├── gtk.dark.png ├── gtk.light.png ├── mac.dark.png ├── mac.light.png ├── qt.light.png ├── win32.dark.png ├── winui.dark.png ├── win32.light.png └── winui.light.png ├── .gitignore ├── winio-ui-windows-common ├── manifest.rc ├── src │ ├── accent.rs │ ├── lib.rs │ ├── resource.rs │ ├── darkmode │ │ └── fallback.rs │ ├── monitor.rs │ ├── backdrop.rs │ └── runtime.rs ├── build.rs ├── app.manifest └── Cargo.toml ├── .clang-format ├── winio-ui-gtk ├── src │ ├── ui │ │ ├── accent.rs │ │ ├── monitor.rs │ │ ├── mod.rs │ │ ├── label.rs │ │ ├── button.rs │ │ └── scroll_view.rs │ ├── lib.rs │ └── runtime.rs └── Cargo.toml ├── winio ├── build.rs ├── src │ ├── stub │ │ ├── ui │ │ │ ├── ext.rs │ │ │ ├── mod.rs │ │ │ ├── button.rs │ │ │ ├── label.rs │ │ │ ├── msgbox.rs │ │ │ ├── scroll_view.rs │ │ │ ├── check_box.rs │ │ │ ├── radio_button.rs │ │ │ ├── filebox.rs │ │ │ ├── text_box.rs │ │ │ ├── progress.rs │ │ │ ├── edit.rs │ │ │ ├── webview.rs │ │ │ ├── widget.rs │ │ │ ├── list_box.rs │ │ │ ├── scroll_bar.rs │ │ │ ├── tab_view.rs │ │ │ ├── slider.rs │ │ │ ├── combo_box.rs │ │ │ ├── window.rs │ │ │ └── media.rs │ │ ├── runtime.rs │ │ └── mod.rs │ ├── ui │ │ ├── mod.rs │ │ ├── ext.rs │ │ ├── msgbox.rs │ │ ├── filebox.rs │ │ └── app.rs │ ├── widgets │ │ ├── mod.rs │ │ ├── view.rs │ │ ├── button.rs │ │ ├── scroll_view.rs │ │ ├── label.rs │ │ ├── check_box.rs │ │ ├── progress.rs │ │ └── text_box.rs │ └── lib.rs └── examples │ └── subviews │ ├── mod.rs │ ├── misc │ └── backdrop_stub.rs │ └── dummy.rs ├── winio-handle ├── src │ └── lib.rs └── Cargo.toml ├── winio-callback └── Cargo.toml ├── winio-primitive ├── src │ ├── lib.rs │ ├── msgbox.rs │ ├── monitor.rs │ └── canvas.rs └── Cargo.toml ├── winio-ui-win32 ├── src │ ├── lib.rs │ └── ui │ │ ├── dpi.rs │ │ ├── button.rs │ │ ├── radio_button.rs │ │ ├── check_box.rs │ │ ├── label.rs │ │ └── mod.rs └── Cargo.toml ├── winio-layout └── Cargo.toml ├── rustfmt.toml ├── winio-pollable └── Cargo.toml ├── winio-elm ├── src │ ├── stream.rs │ └── channel.rs └── Cargo.toml ├── winio-ui-app-kit ├── src │ ├── ui │ │ ├── accent.rs │ │ ├── monitor.rs │ │ ├── label.rs │ │ └── progress.rs │ └── lib.rs └── Cargo.toml ├── LICENSE └── Cargo.toml /winio-ui-qt/src/runtime.rs: -------------------------------------------------------------------------------- 1 | mod qt; 2 | pub use qt::*; 3 | -------------------------------------------------------------------------------- /winio-ui-winui/src/hook/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod mq; 2 | pub(crate) mod mrm; 3 | -------------------------------------------------------------------------------- /assets/qt.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/qt.dark.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | 4 | .vscode/ 5 | .cargo/ 6 | compile_flags.txt 7 | -------------------------------------------------------------------------------- /assets/gtk.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/gtk.dark.png -------------------------------------------------------------------------------- /assets/gtk.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/gtk.light.png -------------------------------------------------------------------------------- /assets/mac.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/mac.dark.png -------------------------------------------------------------------------------- /assets/mac.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/mac.light.png -------------------------------------------------------------------------------- /assets/qt.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/qt.light.png -------------------------------------------------------------------------------- /assets/win32.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/win32.dark.png -------------------------------------------------------------------------------- /assets/winui.dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/winui.dark.png -------------------------------------------------------------------------------- /winio-ui-windows-common/manifest.rc: -------------------------------------------------------------------------------- 1 | #define RT_MANIFEST 24 2 | 1 RT_MANIFEST "app.manifest" -------------------------------------------------------------------------------- /assets/win32.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/win32.light.png -------------------------------------------------------------------------------- /assets/winui.light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/compio-rs/winio/HEAD/assets/winui.light.png -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -4 2 | IndentWidth: 4 3 | TabWidth: 4 4 | UseTab: Never 5 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/monitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | rust::Vec screen_all(); 9 | -------------------------------------------------------------------------------- /winio-ui-gtk/src/ui/accent.rs: -------------------------------------------------------------------------------- 1 | use winio_primitive::Color; 2 | 3 | use crate::{Error, Result}; 4 | 5 | /// Get the accent color. 6 | pub fn accent_color() -> Result { 7 | Err(Error::NotSupported) 8 | } 9 | -------------------------------------------------------------------------------- /winio/build.rs: -------------------------------------------------------------------------------- 1 | use cfg_aliases::cfg_aliases; 2 | 3 | fn main() { 4 | cfg_aliases! { 5 | win32: { all(windows, feature = "win32") }, 6 | winui: { all(windows, feature = "winui") }, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /winio-handle/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Window handle for winio. 2 | 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![warn(missing_docs)] 5 | 6 | mod window; 7 | pub use window::*; 8 | 9 | mod widget; 10 | pub use widget::*; 11 | 12 | mod container; 13 | pub use container::*; 14 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/label.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include "edit.hpp" 5 | #include 6 | 7 | inline std::unique_ptr new_label(QWidget *parent) { 8 | return std::make_unique(parent); 9 | } 10 | 11 | STATIC_CAST_ASSERT(QLabel, QWidget); 12 | -------------------------------------------------------------------------------- /winio-callback/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-callback" 3 | version = "0.1.2" 4 | description = "Callback helper for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/progress.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | 8 | STATIC_CAST_ASSERT(QProgressBar, QWidget); 9 | 10 | inline std::unique_ptr new_progress_bar(QWidget *parent) { 11 | return std::make_unique(parent); 12 | } 13 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/scroll_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | 6 | using QtScrollBarPolicy = Qt::ScrollBarPolicy; 7 | 8 | STATIC_CAST_ASSERT(QScrollArea, QWidget); 9 | 10 | inline std::unique_ptr new_scroll_area(QWidget *parent) { 11 | return std::make_unique(parent); 12 | } 13 | -------------------------------------------------------------------------------- /winio-primitive/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Primitive types for winio. 2 | 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![warn(missing_docs)] 5 | 6 | mod traits; 7 | pub use traits::*; 8 | 9 | mod drawing; 10 | pub use drawing::*; 11 | 12 | mod monitor; 13 | pub use monitor::*; 14 | 15 | mod canvas; 16 | pub use canvas::*; 17 | 18 | mod msgbox; 19 | pub use msgbox::*; 20 | -------------------------------------------------------------------------------- /winio/src/stub/ui/ext.rs: -------------------------------------------------------------------------------- 1 | use winio_primitive::{Color, ColorTheme, Monitor}; 2 | 3 | use crate::stub::{Result, not_impl}; 4 | 5 | pub fn monitor_get_all() -> Result> { 6 | not_impl() 7 | } 8 | 9 | pub fn color_theme() -> Result { 10 | not_impl() 11 | } 12 | 13 | pub fn accent_color() -> Result { 14 | not_impl() 15 | } 16 | -------------------------------------------------------------------------------- /winio/src/ui/mod.rs: -------------------------------------------------------------------------------- 1 | //! Basic UI functionalities and extensions. 2 | 3 | mod app; 4 | mod canvas; 5 | mod ext; 6 | mod filebox; 7 | mod msgbox; 8 | 9 | pub use app::*; 10 | pub use canvas::*; 11 | pub use ext::*; 12 | pub use filebox::*; 13 | pub use msgbox::*; 14 | 15 | #[cfg(feature = "plotters")] 16 | mod plotters; 17 | #[cfg(feature = "plotters")] 18 | pub use plotters::*; 19 | -------------------------------------------------------------------------------- /winio-ui-windows-common/src/accent.rs: -------------------------------------------------------------------------------- 1 | use windows::UI::ViewManagement::{UIColorType, UISettings}; 2 | use winio_primitive::Color; 3 | 4 | /// Get the accent color. 5 | pub fn accent_color() -> crate::Result { 6 | let settings = UISettings::new()?; 7 | let accent = settings.GetColorValue(UIColorType::Accent)?; 8 | Ok(Color::new(accent.R, accent.G, accent.B, accent.A)) 9 | } 10 | -------------------------------------------------------------------------------- /winio-primitive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-primitive" 3 | version = "0.2.0" 4 | description = "Primitive types for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [dependencies] 12 | bitflags = { workspace = true } 13 | euclid = "0.22" 14 | rgb = "0.8" 15 | -------------------------------------------------------------------------------- /winio-ui-win32/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Win32 backend for winio. 2 | 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![cfg_attr(feature = "once_cell_try", feature(once_cell_try))] 5 | #![cfg(windows)] 6 | 7 | pub use winio_ui_windows_common::{Error, Result}; 8 | 9 | scoped_tls::scoped_thread_local!(pub(crate) static RUNTIME: Runtime); 10 | 11 | mod runtime; 12 | pub use runtime::*; 13 | 14 | mod ui; 15 | pub use ui::*; 16 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/monitor.cpp: -------------------------------------------------------------------------------- 1 | #include "monitor.hpp" 2 | #include 3 | 4 | rust::Vec screen_all() { 5 | rust::Vec res{}; 6 | for (QScreen *s : QApplication::screens()) { 7 | res.push_back(Monitor{s->geometry(), s->availableGeometry(), 8 | s->logicalDotsPerInchX(), 9 | s->logicalDotsPerInchY()}); 10 | } 11 | return res; 12 | } 13 | -------------------------------------------------------------------------------- /winio-layout/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-layout" 3 | version = "0.2.0" 4 | description = "Layouts for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [dependencies] 12 | winio-primitive = { workspace = true } 13 | 14 | paste = { workspace = true } 15 | taffy = "0.9" 16 | thiserror = { workspace = true } 17 | -------------------------------------------------------------------------------- /winio/src/stub/runtime.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | 3 | use super::{Result, not_impl}; 4 | 5 | pub struct Runtime; 6 | 7 | impl Runtime { 8 | pub fn new() -> Result { 9 | not_impl() 10 | } 11 | 12 | #[cfg(not(windows))] 13 | pub fn set_app_id(&mut self, _app_id: &str) -> Result<()> { 14 | not_impl() 15 | } 16 | 17 | pub fn block_on(&self, _future: F) -> F::Output { 18 | not_impl() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/list_box.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | STATIC_CAST_ASSERT(QListWidget, QWidget); 11 | 12 | std::unique_ptr new_list_widget(QWidget *parent); 13 | void list_widget_connect_select(QListWidget &w, callback_fn_t callback, 14 | std::uint8_t const *data); 15 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | unstable_features = true 2 | 3 | style_edition = "2024" 4 | 5 | group_imports = "StdExternalCrate" 6 | imports_granularity = "Crate" 7 | reorder_imports = true 8 | 9 | wrap_comments = true 10 | normalize_comments = true 11 | 12 | reorder_impl_items = true 13 | condense_wildcard_suffixes = true 14 | enum_discrim_align_threshold = 20 15 | use_field_init_shorthand = true 16 | 17 | format_strings = true 18 | format_code_in_doc_comments = true 19 | format_macro_matchers = true 20 | -------------------------------------------------------------------------------- /winio-ui-windows-common/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rerun-if-changed=build.rs"); 3 | 4 | let target_os = std::env::var("CARGO_CFG_TARGET_OS"); 5 | if target_os.as_deref() == Ok("windows") { 6 | println!("cargo:rerun-if-changed=app.manifest"); 7 | println!("cargo:rerun-if-changed=manifest.rc"); 8 | embed_resource::compile("manifest.rc", embed_resource::NONE) 9 | .manifest_required() 10 | .unwrap(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/tab_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | 6 | STATIC_CAST_ASSERT(QTabWidget, QWidget); 7 | 8 | std::unique_ptr new_tab_widget(QWidget *parent); 9 | 10 | void tab_widget_connect_changed(QTabWidget &w, callback_fn_t callback, 11 | std::uint8_t const *data); 12 | 13 | inline int tab_widget_index_of(QTabWidget const &w, QWidget *widget) { 14 | return w.indexOf(widget); 15 | } 16 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/widget.cpp: -------------------------------------------------------------------------------- 1 | #include "widget.hpp" 2 | #include 3 | 4 | bool is_dark() { 5 | auto back = QApplication::palette().color(QPalette::Window); 6 | auto brightness = 7 | back.redF() * 0.299 + back.greenF() * 0.587 + back.blueF() * 0.114; 8 | return brightness < 0.5; 9 | } 10 | 11 | std::unique_ptr new_widget(QWidget *parent) { 12 | return std::make_unique(parent); 13 | } 14 | 15 | QPointer widget_weak(QWidget *w) { return QPointer(w); } 16 | -------------------------------------------------------------------------------- /winio-pollable/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-pollable" 3 | version = "0.3.0" 4 | description = "A thin wrapper over compio-runtime to make it pollable." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [dependencies] 12 | compio = { workspace = true, features = ["runtime"] } 13 | 14 | [target.'cfg(target_os = "linux")'.dependencies] 15 | rustix = { version = "1", features = ["event"] } 16 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/tab_view.cpp: -------------------------------------------------------------------------------- 1 | #include "tab_view.hpp" 2 | 3 | std::unique_ptr new_tab_widget(QWidget *parent) { 4 | auto widget = std::make_unique(parent); 5 | widget->setTabsClosable(false); 6 | return widget; 7 | } 8 | 9 | void tab_widget_connect_changed(QTabWidget &w, callback_fn_t callback, 10 | std::uint8_t const *data) { 11 | QObject::connect(&w, &QTabWidget::currentChanged, 12 | [callback, data](int) { callback(data); }); 13 | } 14 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | bool is_dark(); 10 | 11 | std::unique_ptr new_widget(QWidget *parent); 12 | 13 | using QWidgetPointer = QPointer; 14 | 15 | namespace rust { 16 | template <> struct IsRelocatable : std::true_type {}; 17 | } // namespace rust 18 | 19 | static_assert(sizeof(QWidgetPointer) == 2 * sizeof(std::size_t)); 20 | 21 | QWidgetPointer widget_weak(QWidget *w); 22 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/list_box.cpp: -------------------------------------------------------------------------------- 1 | #include "list_box.hpp" 2 | 3 | std::unique_ptr new_list_widget(QWidget *parent) { 4 | auto list = std::make_unique(parent); 5 | list->setSelectionMode(QAbstractItemView::MultiSelection); 6 | return list; 7 | } 8 | 9 | void list_widget_connect_select(QListWidget &w, callback_fn_t callback, 10 | std::uint8_t const *data) { 11 | QObject::connect(&w, &QListWidget::itemSelectionChanged, 12 | [callback, data]() { callback(data); }); 13 | } 14 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/msgbox.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | 7 | using QMessageBoxIcon = QMessageBox::Icon; 8 | using QMessageBoxStandardButton = QMessageBox::StandardButton; 9 | 10 | std::unique_ptr new_message_box(QWidget *parent); 11 | void message_box_connect_finished(QMessageBox &b, 12 | callback_fn_t callback, 13 | std::uint8_t const *data); 14 | QPushButton *message_box_add_button(QMessageBox &b, rust::Str text); 15 | -------------------------------------------------------------------------------- /winio/src/stub/mod.rs: -------------------------------------------------------------------------------- 1 | /// A stub error type. 2 | #[derive(Debug)] 3 | pub struct Error(std::convert::Infallible); 4 | 5 | impl std::fmt::Display for Error { 6 | fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 7 | not_impl() 8 | } 9 | } 10 | 11 | impl std::error::Error for Error {} 12 | 13 | /// A stub result type. 14 | pub type Result = std::result::Result; 15 | 16 | pub fn not_impl() -> ! { 17 | unimplemented!("stub implementation") 18 | } 19 | 20 | mod runtime; 21 | pub use runtime::*; 22 | 23 | mod ui; 24 | pub use ui::*; 25 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/filebox.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | 8 | using QFileDialogAcceptMode = QFileDialog::AcceptMode; 9 | using QFileDialogFileMode = QFileDialog::FileMode; 10 | 11 | std::unique_ptr new_file_dialog(QWidget *parent); 12 | void file_dialog_connect_finished(QFileDialog &b, 13 | callback_fn_t callback, 14 | std::uint8_t const *data); 15 | rust::Vec file_dialog_files(QFileDialog const &b); 16 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/combo_box.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | 8 | STATIC_CAST_ASSERT(QComboBox, QWidget); 9 | 10 | std::unique_ptr new_combo_box(QWidget *parent); 11 | void combo_box_connect_changed(QComboBox &w, callback_fn_t callback, 12 | std::uint8_t const *data); 13 | void combo_box_connect_select(QComboBox &w, callback_fn_t callback, 14 | std::uint8_t const *data); 15 | 16 | inline void combo_box_insert(QComboBox &w, int i, const QString &s) { 17 | w.insertItem(i, s); 18 | } 19 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/webview.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | 7 | STATIC_CAST_ASSERT(QWebEngineView, QWidget); 8 | 9 | std::unique_ptr new_webview(QWidget *parent); 10 | 11 | void webview_connect_load_started(QWebEngineView &w, 12 | callback_fn_t callback, 13 | std::uint8_t const *data); 14 | 15 | void webview_connect_load_finished(QWebEngineView &w, 16 | callback_fn_t callback, 17 | std::uint8_t const *data); 18 | -------------------------------------------------------------------------------- /winio-ui-winui/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! WinUI backend for winio. 2 | 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![cfg_attr(feature = "once_cell_try", feature(once_cell_try))] 5 | #![cfg(windows)] 6 | 7 | use winio_callback::Runnable; 8 | pub use winio_ui_windows_common::{Error, Result}; 9 | 10 | pub(crate) struct GlobalRuntime; 11 | 12 | impl Runnable for GlobalRuntime { 13 | #[inline] 14 | fn run() { 15 | RUNTIME.with(|runtime| runtime.run()); 16 | } 17 | } 18 | 19 | scoped_tls::scoped_thread_local!(pub(crate) static RUNTIME: Runtime); 20 | 21 | mod runtime; 22 | pub use runtime::*; 23 | 24 | mod ui; 25 | pub use ui::*; 26 | 27 | mod hook; 28 | -------------------------------------------------------------------------------- /winio-ui-windows-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Windows common methods for winio. 2 | 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![cfg_attr(feature = "once_cell_try", feature(once_cell_try))] 5 | #![cfg(windows)] 6 | 7 | pub use windows::core::{Error, Result}; 8 | 9 | mod accent; 10 | pub use accent::*; 11 | 12 | mod filebox; 13 | pub use filebox::*; 14 | 15 | mod msgbox; 16 | pub use msgbox::*; 17 | 18 | mod monitor; 19 | pub use monitor::*; 20 | 21 | mod canvas; 22 | pub use canvas::*; 23 | 24 | mod darkmode; 25 | pub use darkmode::*; 26 | 27 | mod resource; 28 | pub use resource::*; 29 | 30 | mod backdrop; 31 | pub use backdrop::*; 32 | 33 | mod runtime; 34 | pub use runtime::*; 35 | -------------------------------------------------------------------------------- /winio-ui-qt/src/runtime/qt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | struct WinioQtEventLoop { 11 | rust::Vec m_args; 12 | std::vector m_args_ptr; 13 | int m_argc; 14 | QApplication m_app; 15 | QSocketNotifier m_notifier; 16 | 17 | WinioQtEventLoop(rust::Vec args, int fd); 18 | 19 | void process(); 20 | void process(int maxtime); 21 | 22 | void setAppName(rust::Str name); 23 | }; 24 | 25 | std::unique_ptr new_event_loop(rust::Vec args, 26 | int fd); -------------------------------------------------------------------------------- /winio-ui-windows-common/src/resource.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::null_mut; 2 | 3 | use windows_sys::Win32::{ 4 | Foundation::HMODULE, 5 | System::LibraryLoader::{ 6 | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 7 | GetModuleHandleExW, 8 | }, 9 | }; 10 | 11 | /// Get the handle of the current executable or DLL. 12 | pub fn get_current_module_handle() -> HMODULE { 13 | let mut module: HMODULE = null_mut(); 14 | unsafe { 15 | GetModuleHandleExW( 16 | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 17 | get_current_module_handle as *const _, 18 | &mut module, 19 | ) 20 | }; 21 | module 22 | } 23 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/edit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using QtAlignmentFlag = Qt::Alignment; 10 | using QLineEditEchoMode = QLineEdit::EchoMode; 11 | 12 | STATIC_CAST_ASSERT(QLineEdit, QWidget); 13 | STATIC_CAST_ASSERT(QTextEdit, QWidget); 14 | 15 | std::unique_ptr new_line_edit(QWidget *parent); 16 | void line_edit_connect_changed(QLineEdit &w, callback_fn_t callback, 17 | std::uint8_t const *data); 18 | 19 | std::unique_ptr new_text_edit(QWidget *parent); 20 | void text_edit_connect_changed(QTextEdit &w, callback_fn_t callback, 21 | std::uint8_t const *data); 22 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/scroll_bar.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using QtOrientation = Qt::Orientation; 11 | using QSliderTickPosition = QSlider::TickPosition; 12 | 13 | STATIC_CAST_ASSERT(QAbstractSlider, QWidget); 14 | STATIC_CAST_ASSERT(QScrollBar, QAbstractSlider); 15 | STATIC_CAST_ASSERT(QSlider, QAbstractSlider); 16 | 17 | std::unique_ptr new_scroll_bar(QWidget *parent); 18 | std::unique_ptr new_slider(QWidget *parent); 19 | 20 | void scroll_bar_connect_moved(QAbstractSlider &w, 21 | callback_fn_t callback, 22 | std::uint8_t const *data); 23 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/combo_box.cpp: -------------------------------------------------------------------------------- 1 | #include "combo_box.hpp" 2 | #include 3 | 4 | std::unique_ptr new_combo_box(QWidget *parent) { 5 | return std::make_unique(parent); 6 | } 7 | 8 | void combo_box_connect_changed(QComboBox &w, callback_fn_t callback, 9 | std::uint8_t const *data) { 10 | QObject::connect(&w, &QComboBox::currentTextChanged, 11 | [callback, data](QString const &) { callback(data); }); 12 | } 13 | 14 | void combo_box_connect_select(QComboBox &w, callback_fn_t callback, 15 | std::uint8_t const *data) { 16 | QObject::connect(&w, QOverload::of(&QComboBox::currentIndexChanged), 17 | [callback, data](int) { callback(data); }); 18 | } 19 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/msgbox.cpp: -------------------------------------------------------------------------------- 1 | #include "msgbox.hpp" 2 | 3 | std::unique_ptr new_message_box(QWidget *parent) { 4 | auto box = std::make_unique(parent); 5 | box->setWindowModality(Qt::WindowModal); 6 | return box; 7 | } 8 | 9 | void message_box_connect_finished(QMessageBox &b, 10 | callback_fn_t callback, 11 | std::uint8_t const *data) { 12 | QObject::connect(&b, &QMessageBox::finished, 13 | [callback, data](int result) { callback(data, result); }); 14 | } 15 | 16 | QPushButton *message_box_add_button(QMessageBox &b, rust::Str text) { 17 | return b.addButton(QString::fromUtf8(text.data(), text.size()), 18 | QMessageBox::AcceptRole); 19 | } 20 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/button.cpp: -------------------------------------------------------------------------------- 1 | #include "button.hpp" 2 | 3 | std::unique_ptr new_push_button(QWidget *parent) { 4 | return std::make_unique(parent); 5 | } 6 | 7 | std::unique_ptr new_check_box(QWidget *parent) { 8 | return std::make_unique(parent); 9 | } 10 | 11 | std::unique_ptr new_radio_button(QWidget *parent) { 12 | auto button = std::make_unique(parent); 13 | button->setAutoExclusive(false); 14 | return button; 15 | } 16 | 17 | void push_button_connect_clicked(QAbstractButton &w, 18 | callback_fn_t callback, 19 | std::uint8_t const *data) { 20 | QObject::connect(&w, &QAbstractButton::clicked, 21 | [callback, data](bool) { callback(data); }); 22 | } 23 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/webview.cpp: -------------------------------------------------------------------------------- 1 | #include "webview.hpp" 2 | 3 | std::unique_ptr new_webview(QWidget *parent) { 4 | return std::make_unique(parent); 5 | } 6 | 7 | void webview_connect_load_started(QWebEngineView &w, 8 | callback_fn_t callback, 9 | std::uint8_t const *data) { 10 | QObject::connect(&w, &QWebEngineView::loadStarted, 11 | [callback, data]() { callback(data); }); 12 | } 13 | 14 | void webview_connect_load_finished(QWebEngineView &w, 15 | callback_fn_t callback, 16 | std::uint8_t const *data) { 17 | QObject::connect(&w, &QWebEngineView::loadFinished, 18 | [callback, data](bool) { callback(data); }); 19 | } 20 | -------------------------------------------------------------------------------- /winio-ui-windows-common/src/darkmode/fallback.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::missing_safety_doc)] 2 | 3 | use windows_sys::Win32::{ 4 | Foundation::{HWND, LPARAM}, 5 | UI::Controls::PFTASKDIALOGCALLBACK, 6 | }; 7 | 8 | use super::PreferredAppMode; 9 | use crate::Result; 10 | 11 | pub fn is_dark_mode_allowed_for_app() -> bool { 12 | false 13 | } 14 | 15 | pub(crate) const TASK_DIALOG_CALLBACK: PFTASKDIALOGCALLBACK = None; 16 | 17 | pub unsafe fn control_use_dark_mode(_: HWND, _: bool) -> Result<()> { 18 | Ok(()) 19 | } 20 | 21 | pub fn set_preferred_app_mode(_: PreferredAppMode) -> PreferredAppMode { 22 | PreferredAppMode::Default 23 | } 24 | 25 | pub unsafe fn window_use_dark_mode(_: HWND) -> Result<()> { 26 | Ok(()) 27 | } 28 | 29 | pub unsafe fn children_refresh_dark_mode(_: HWND, _: LPARAM) {} 30 | 31 | pub fn init_dark() {} 32 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/edit.cpp: -------------------------------------------------------------------------------- 1 | #include "edit.hpp" 2 | 3 | std::unique_ptr new_line_edit(QWidget *parent) { 4 | return std::make_unique(parent); 5 | } 6 | 7 | void line_edit_connect_changed(QLineEdit &w, callback_fn_t callback, 8 | std::uint8_t const *data) { 9 | QObject::connect(&w, &QLineEdit::textEdited, 10 | [callback, data](QString const &) { callback(data); }); 11 | } 12 | 13 | std::unique_ptr new_text_edit(QWidget *parent) { 14 | return std::make_unique(parent); 15 | } 16 | 17 | void text_edit_connect_changed(QTextEdit &w, callback_fn_t callback, 18 | std::uint8_t const *data) { 19 | QObject::connect(&w, &QTextEdit::textChanged, 20 | [callback, data]() { callback(data); }); 21 | } 22 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/button.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using QtCheckState = Qt::CheckState; 12 | 13 | STATIC_CAST_ASSERT(QAbstractButton, QWidget); 14 | STATIC_CAST_ASSERT(QPushButton, QAbstractButton); 15 | STATIC_CAST_ASSERT(QCheckBox, QAbstractButton); 16 | STATIC_CAST_ASSERT(QRadioButton, QAbstractButton); 17 | 18 | std::unique_ptr new_push_button(QWidget *parent); 19 | std::unique_ptr new_check_box(QWidget *parent); 20 | std::unique_ptr new_radio_button(QWidget *parent); 21 | 22 | void push_button_connect_clicked(QAbstractButton &w, 23 | callback_fn_t callback, 24 | std::uint8_t const *data); 25 | -------------------------------------------------------------------------------- /winio-ui-windows-common/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | PerMonitorV2 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /winio-elm/src/stream.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | async_iter::AsyncIterator, 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | }; 6 | 7 | use futures_util::Stream; 8 | 9 | macro_rules! stream { 10 | ($($t:tt)*) => { 11 | $crate::stream::AsyncIteratorStream::new(async gen { $($t)* }) 12 | } 13 | } 14 | 15 | pub(crate) use stream; 16 | 17 | pub(crate) struct AsyncIteratorStream(I); 18 | 19 | impl AsyncIteratorStream { 20 | pub fn new(iter: I) -> Self { 21 | Self(iter) 22 | } 23 | } 24 | 25 | impl Stream for AsyncIteratorStream { 26 | type Item = I::Item; 27 | 28 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 29 | unsafe { self.map_unchecked_mut(|this| &mut this.0) }.poll_next(cx) 30 | } 31 | 32 | fn size_hint(&self) -> (usize, Option) { 33 | self.0.size_hint() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/filebox.cpp: -------------------------------------------------------------------------------- 1 | #include "filebox.hpp" 2 | 3 | std::unique_ptr new_file_dialog(QWidget *parent) { 4 | auto box = std::make_unique(parent); 5 | box->setWindowModality(Qt::WindowModal); 6 | return box; 7 | } 8 | 9 | void file_dialog_connect_finished(QFileDialog &b, 10 | callback_fn_t callback, 11 | std::uint8_t const *data) { 12 | QObject::connect(&b, &QFileDialog::finished, 13 | [callback, data](int result) { callback(data, result); }); 14 | } 15 | 16 | rust::Vec file_dialog_files(QFileDialog const &b) { 17 | rust::Vec results{}; 18 | for (QString &f : b.selectedFiles()) { 19 | results.push_back( 20 | rust::String{(const char16_t *)f.utf16(), (std::size_t)f.size()}); 21 | } 22 | return results; 23 | } 24 | -------------------------------------------------------------------------------- /winio-elm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-elm" 3 | version = "0.4.0" 4 | description = "ELM framework for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [dependencies] 12 | winio-primitive = { workspace = true, optional = true } 13 | winio-handle = { workspace = true, optional = true } 14 | 15 | async-stream = "0.3" 16 | futures-util = { workspace = true } 17 | inherit-methods-macro = { workspace = true, optional = true } 18 | paste = { workspace = true } 19 | smallvec = "1" 20 | tuplex = { workspace = true } 21 | 22 | [dev-dependencies] 23 | compio = { workspace = true, features = ["macros"] } 24 | 25 | [features] 26 | handle = ["dep:winio-handle"] 27 | primitive = ["dep:winio-primitive", "dep:inherit-methods-macro"] 28 | 29 | gen_blocks = [] 30 | nightly = ["gen_blocks"] 31 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/scroll_bar.cpp: -------------------------------------------------------------------------------- 1 | #include "scroll_bar.hpp" 2 | 3 | std::unique_ptr new_scroll_bar(QWidget *parent) { 4 | auto bar = std::make_unique(parent); 5 | bar->setTracking(true); 6 | bar->setOrientation(Qt::Horizontal); 7 | return bar; 8 | } 9 | 10 | std::unique_ptr new_slider(QWidget *parent) { 11 | auto bar = std::make_unique(parent); 12 | bar->setTracking(true); 13 | bar->setOrientation(Qt::Horizontal); 14 | bar->setTickPosition(QSlider::TicksBothSides); 15 | bar->setPageStep(0); 16 | return bar; 17 | } 18 | 19 | void scroll_bar_connect_moved(QAbstractSlider &w, 20 | callback_fn_t callback, 21 | std::uint8_t const *data) { 22 | QObject::connect(&w, &QAbstractSlider::valueChanged, 23 | [&w, callback, data](int) { callback(data, w); }); 24 | } 25 | -------------------------------------------------------------------------------- /winio-ui-app-kit/src/ui/accent.rs: -------------------------------------------------------------------------------- 1 | use objc2_app_kit::{NSColor, NSColorSpace}; 2 | use winio_primitive::Color; 3 | 4 | use crate::{Error, Result, catch}; 5 | 6 | /// Get the accent color. 7 | pub fn accent_color() -> Result { 8 | catch(|| { 9 | let accent = NSColor::controlAccentColor(); 10 | let color_space = NSColorSpace::genericRGBColorSpace(); 11 | let accent = accent 12 | .colorUsingColorSpace(&color_space) 13 | .ok_or(Error::NullPointer)?; 14 | let mut r: f64 = 0.0; 15 | let mut g: f64 = 0.0; 16 | let mut b: f64 = 0.0; 17 | let mut a: f64 = 0.0; 18 | unsafe { 19 | accent.getRed_green_blue_alpha(&mut r, &mut g, &mut b, &mut a); 20 | } 21 | Ok(Color::new( 22 | (r * 255.0) as u8, 23 | (g * 255.0) as u8, 24 | (b * 255.0) as u8, 25 | (a * 255.0) as u8, 26 | )) 27 | }) 28 | .flatten() 29 | } 30 | -------------------------------------------------------------------------------- /winio-ui-gtk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-ui-gtk" 3 | version = "0.4.0" 4 | description = "GTK backend for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [target.'cfg(not(any(windows, target_os = "macos")))'.dependencies] 12 | winio-primitive = { workspace = true } 13 | winio-handle = { workspace = true, features = ["gtk"] } 14 | winio-callback = { workspace = true } 15 | winio-pollable = { workspace = true } 16 | 17 | compio-log = { workspace = true } 18 | 19 | inherit-methods-macro = { workspace = true } 20 | image = { workspace = true } 21 | scoped-tls = { workspace = true } 22 | thiserror = { workspace = true } 23 | 24 | gtk4 = { workspace = true, features = ["v4_14"] } 25 | pangocairo = "0.21" 26 | webkit6 = { version = "0.5", optional = true } 27 | 28 | [features] 29 | media = [] 30 | webview = ["dep:webkit6"] 31 | -------------------------------------------------------------------------------- /winio/src/ui/ext.rs: -------------------------------------------------------------------------------- 1 | use winio_primitive::{Color, ColorTheme, Monitor}; 2 | 3 | use crate::sys::Result; 4 | 5 | /// Extension trait for [`Monitor`]. 6 | pub trait MonitorExt: Sized { 7 | /// Retrieve all monitors information. 8 | fn all() -> Result>; 9 | } 10 | 11 | impl MonitorExt for Monitor { 12 | fn all() -> Result> { 13 | crate::sys::monitor_get_all() 14 | } 15 | } 16 | 17 | /// Extension trait for [`ColorTheme`]. 18 | pub trait ColorThemeExt: Sized { 19 | /// Get current color theme. 20 | fn current() -> Result; 21 | } 22 | 23 | impl ColorThemeExt for ColorTheme { 24 | fn current() -> Result { 25 | crate::sys::color_theme() 26 | } 27 | } 28 | 29 | /// Extension trait for [`Color`]. 30 | pub trait ColorExt: Sized { 31 | /// Get accent color. 32 | fn accent() -> Result; 33 | } 34 | 35 | impl ColorExt for Color { 36 | fn accent() -> Result { 37 | crate::sys::accent_color() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /winio/examples/subviews/mod.rs: -------------------------------------------------------------------------------- 1 | mod fs; 2 | pub use fs::*; 3 | 4 | mod net; 5 | pub use net::*; 6 | 7 | mod gallery; 8 | pub use gallery::*; 9 | 10 | mod scroll_view; 11 | pub use scroll_view::*; 12 | 13 | mod misc; 14 | pub use misc::*; 15 | 16 | cfg_if::cfg_if! { 17 | if #[cfg(feature = "plotters")] { 18 | mod plotters; 19 | pub use plotters::*; 20 | } 21 | } 22 | 23 | cfg_if::cfg_if! { 24 | if #[cfg(feature = "media")] { 25 | mod media; 26 | pub use media::*; 27 | } 28 | } 29 | 30 | cfg_if::cfg_if! { 31 | if #[cfg(feature = "webview")] { 32 | mod failable_webview; 33 | pub use failable_webview::*; 34 | 35 | mod webview; 36 | pub use webview::*; 37 | 38 | mod markdown; 39 | pub use markdown::*; 40 | } 41 | } 42 | 43 | cfg_if::cfg_if! { 44 | if #[cfg(any(not(feature = "media"), not(feature = "webview"), not(feature = "plotters")))] { 45 | mod dummy; 46 | pub use dummy::*; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /winio-ui-gtk/src/ui/monitor.rs: -------------------------------------------------------------------------------- 1 | use gtk4::{ 2 | gdk::{ 3 | self, 4 | prelude::{DisplayExt, MonitorExt}, 5 | }, 6 | glib::object::Cast, 7 | }; 8 | use winio_primitive::{Monitor, Point, Rect, Size}; 9 | 10 | use crate::Result; 11 | 12 | pub fn monitor_get_all() -> Result> { 13 | Ok(gdk::DisplayManager::get() 14 | .list_displays() 15 | .into_iter() 16 | .flat_map(|d| { 17 | d.monitors() 18 | .into_iter() 19 | .filter_map(|m| m.ok().and_then(|m| m.downcast::().ok())) 20 | .collect::>() 21 | }) 22 | .map(|m| { 23 | let geo = rect_from(m.geometry()); 24 | let scale = m.scale(); 25 | Monitor::new(geo * scale, geo * scale, Size::new(scale, scale)) 26 | }) 27 | .collect()) 28 | } 29 | 30 | #[inline] 31 | fn rect_from(r: gdk::Rectangle) -> Rect { 32 | Rect::new( 33 | Point::new(r.x() as _, r.y() as _), 34 | Size::new(r.width() as _, r.height() as _), 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /winio-ui-qt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-ui-qt" 3 | version = "0.4.0" 4 | description = "Qt backend for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [target.'cfg(not(any(windows, target_os = "macos")))'.dependencies] 12 | winio-primitive = { workspace = true } 13 | winio-handle = { workspace = true, features = ["qt"] } 14 | winio-callback = { workspace = true } 15 | winio-pollable = { workspace = true } 16 | 17 | compio-log = { workspace = true } 18 | 19 | bitflags = { workspace = true } 20 | inherit-methods-macro = { workspace = true } 21 | image = { workspace = true } 22 | local-sync = { workspace = true } 23 | scoped-tls = { workspace = true } 24 | thiserror = { workspace = true } 25 | 26 | cxx = { version = "1.0.160", features = ["c++17"] } 27 | 28 | [build-dependencies] 29 | cxx-build = { version = "1.0.160" } 30 | qt-build-utils = { version = "0.7" } 31 | 32 | [features] 33 | media = [] 34 | webview = [] 35 | 36 | opengl = [] 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Berrysoft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /winio/src/stub/ui/mod.rs: -------------------------------------------------------------------------------- 1 | mod ext; 2 | pub use ext::*; 3 | 4 | mod window; 5 | pub use window::*; 6 | 7 | mod widget; 8 | pub(crate) use widget::*; 9 | 10 | mod canvas; 11 | pub use canvas::*; 12 | 13 | mod msgbox; 14 | pub use msgbox::*; 15 | 16 | mod filebox; 17 | pub use filebox::*; 18 | 19 | mod button; 20 | pub use button::*; 21 | 22 | mod edit; 23 | pub use edit::*; 24 | 25 | mod text_box; 26 | pub use text_box::*; 27 | 28 | mod label; 29 | pub use label::*; 30 | 31 | mod progress; 32 | pub use progress::*; 33 | 34 | mod combo_box; 35 | pub use combo_box::*; 36 | 37 | mod list_box; 38 | pub use list_box::*; 39 | 40 | mod check_box; 41 | pub use check_box::*; 42 | 43 | mod radio_button; 44 | pub use radio_button::*; 45 | 46 | mod scroll_bar; 47 | pub use scroll_bar::*; 48 | 49 | mod scroll_view; 50 | pub use scroll_view::*; 51 | 52 | mod slider; 53 | pub use slider::*; 54 | 55 | mod tab_view; 56 | pub use tab_view::*; 57 | 58 | #[cfg(feature = "media")] 59 | mod media; 60 | #[cfg(feature = "media")] 61 | pub use media::*; 62 | 63 | #[cfg(feature = "webview")] 64 | mod webview; 65 | #[cfg(feature = "webview")] 66 | pub use webview::*; 67 | -------------------------------------------------------------------------------- /winio-handle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-handle" 3 | version = "0.5.0" 4 | description = "Window handle for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [dependencies] 12 | cfg-if = { workspace = true } 13 | raw-window-handle = { version = "0.6", optional = true } 14 | 15 | [target.'cfg(windows)'.dependencies] 16 | windows-sys = { workspace = true, features = ["Win32_Foundation"] } 17 | winui3 = { workspace = true, optional = true, features = [ 18 | "native", 19 | "UI_Composition", 20 | "UI_Xaml_Controls", 21 | ] } 22 | windows-core = { workspace = true } 23 | 24 | [target.'cfg(target_os = "macos")'.dependencies] 25 | objc2 = { workspace = true } 26 | objc2-app-kit = { workspace = true, features = ["NSWindow"] } 27 | 28 | [target.'cfg(not(any(windows, target_os = "macos")))'.dependencies] 29 | gtk4 = { workspace = true, optional = true } 30 | 31 | [features] 32 | win32 = [] 33 | winui = ["dep:winui3"] 34 | qt = [] 35 | gtk = ["dep:gtk4"] 36 | 37 | raw-window-handle = ["dep:raw-window-handle"] 38 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/monitor.rs: -------------------------------------------------------------------------------- 1 | use winio_primitive::{Monitor, Point, Rect, Size}; 2 | 3 | use crate::{Result, ui::QRect}; 4 | 5 | pub fn monitor_get_all() -> Result> { 6 | Ok(ffi::screen_all()? 7 | .into_iter() 8 | .map(|m| { 9 | let dpi = Size::new(m.dpix / 96.0, m.dpiy / 96.0); 10 | Monitor::new(rect_from(m.geo, dpi), rect_from(m.avail_geo, dpi), dpi) 11 | }) 12 | .collect()) 13 | } 14 | 15 | #[inline] 16 | fn rect_from(r: QRect, dpi: Size) -> Rect { 17 | Rect::new( 18 | Point::new(r.x1 as f64 * dpi.width, r.y1 as f64 * dpi.height), 19 | Size::new( 20 | (r.x2 - r.x1) as f64 * dpi.width, 21 | (r.y2 - r.y1) as f64 * dpi.height, 22 | ), 23 | ) 24 | } 25 | 26 | #[cxx::bridge] 27 | mod ffi { 28 | struct Monitor { 29 | geo: QRect, 30 | avail_geo: QRect, 31 | dpix: f64, 32 | dpiy: f64, 33 | } 34 | 35 | unsafe extern "C++-unwind" { 36 | include!("winio-ui-qt/src/ui/monitor.hpp"); 37 | 38 | type QRect = super::QRect; 39 | 40 | fn screen_all() -> Result>; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /winio/examples/subviews/misc/backdrop_stub.rs: -------------------------------------------------------------------------------- 1 | use winio::prelude::{Error as SysError, Result as SysResult, *}; 2 | 3 | use crate::{Error, Result}; 4 | 5 | pub struct BackdropChooser {} 6 | 7 | #[derive(Debug)] 8 | pub enum BackdropChooserEvent {} 9 | 10 | #[derive(Debug)] 11 | pub enum BackdropChooserMessage {} 12 | 13 | impl Component for BackdropChooser { 14 | type Error = Error; 15 | type Event = BackdropChooserEvent; 16 | type Init<'a> = BorrowedContainer<'a>; 17 | type Message = BackdropChooserMessage; 18 | 19 | async fn init(_init: Self::Init<'_>, _sender: &ComponentSender) -> Result { 20 | Ok(Self {}) 21 | } 22 | } 23 | 24 | impl Failable for BackdropChooser { 25 | type Error = SysError; 26 | } 27 | 28 | impl Layoutable for BackdropChooser { 29 | fn loc(&self) -> SysResult { 30 | Ok(Point::zero()) 31 | } 32 | 33 | fn set_loc(&mut self, _p: Point) -> SysResult<()> { 34 | Ok(()) 35 | } 36 | 37 | fn size(&self) -> SysResult { 38 | Ok(Size::zero()) 39 | } 40 | 41 | fn set_size(&mut self, _s: Size) -> SysResult<()> { 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /winio-primitive/src/msgbox.rs: -------------------------------------------------------------------------------- 1 | /// Style of message box. 2 | #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] 3 | pub enum MessageBoxStyle { 4 | /// Simple message box. 5 | #[default] 6 | None, 7 | /// Show information. 8 | Info, 9 | /// Show warning message. 10 | Warning, 11 | /// Show error message. 12 | Error, 13 | } 14 | 15 | bitflags::bitflags! { 16 | /// The pre-defined message box buttons. 17 | #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] 18 | pub struct MessageBoxButton: i32 { 19 | /// "Ok" 20 | const Ok = 1 << 0; 21 | /// "Yes" 22 | const Yes = 1 << 1; 23 | /// "No" 24 | const No = 1 << 2; 25 | /// "Cancel" 26 | const Cancel = 1 << 3; 27 | /// "Retry" 28 | const Retry = 1 << 4; 29 | /// "Close" 30 | const Close = 1 << 5; 31 | } 32 | } 33 | 34 | /// Response of message box. 35 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 36 | pub enum MessageBoxResponse { 37 | /// "Cancel" 38 | Cancel, 39 | /// "No" 40 | No, 41 | /// "Ok" 42 | Ok, 43 | /// "Retry" 44 | Retry, 45 | /// "Yes" 46 | Yes, 47 | /// "Close" 48 | Close, 49 | /// Custom response. 50 | Custom(u16), 51 | } 52 | -------------------------------------------------------------------------------- /winio/src/stub/ui/button.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct Button { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl Button { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn loc(&self) -> Result; 29 | 30 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 31 | 32 | pub fn size(&self) -> Result; 33 | 34 | pub fn set_size(&mut self, v: Size) -> Result<()>; 35 | 36 | pub fn tooltip(&self) -> Result; 37 | 38 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 39 | 40 | pub fn text(&self) -> Result; 41 | 42 | pub fn set_text(&mut self, s: impl AsRef) -> Result<()>; 43 | 44 | pub async fn wait_click(&self) { 45 | not_impl() 46 | } 47 | } 48 | 49 | winio_handle::impl_as_widget!(Button, handle); 50 | -------------------------------------------------------------------------------- /winio-ui-qt/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Qt backend for winio. 2 | 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![cfg(not(any(windows, target_os = "macos")))] 5 | 6 | use winio_callback::Runnable; 7 | 8 | pub(crate) struct GlobalRuntime; 9 | 10 | impl Runnable for GlobalRuntime { 11 | #[inline] 12 | fn run() { 13 | RUNTIME.with(|runtime| runtime.run()) 14 | } 15 | } 16 | 17 | scoped_tls::scoped_thread_local!(pub(crate) static RUNTIME: Runtime); 18 | 19 | mod runtime; 20 | pub use runtime::*; 21 | 22 | mod ui; 23 | pub use ui::*; 24 | 25 | #[derive(Debug, thiserror::Error)] 26 | #[non_exhaustive] 27 | pub enum Error { 28 | /// IO error. 29 | #[error("IO error: {0}")] 30 | Io(#[from] std::io::Error), 31 | /// C++ exception. 32 | #[error("C++ exception: {0}")] 33 | Cxx(#[from] cxx::Exception), 34 | /// Index error. 35 | #[error("Index error: {0}")] 36 | Index(usize), 37 | /// Channel recv error. 38 | #[error("Channel recv error: {0}")] 39 | ChannelRecv(#[from] local_sync::oneshot::error::RecvError), 40 | /// Media player error. 41 | #[cfg(feature = "media")] 42 | #[error("Media player error: {0:?}")] 43 | Media(#[from] ui::QMediaPlayerError), 44 | /// Feature not supported. 45 | #[error("Feature not supported")] 46 | NotSupported, 47 | } 48 | 49 | /// Result type for Qt. 50 | pub type Result = std::result::Result; 51 | -------------------------------------------------------------------------------- /winio/src/widgets/mod.rs: -------------------------------------------------------------------------------- 1 | //! ELM widgets. 2 | 3 | use winio_primitive::{Point, Size}; 4 | 5 | fn approx_eq_point(p1: Point, p2: Point) -> bool { 6 | approx_eq(p1.x, p2.x) && approx_eq(p1.y, p2.y) 7 | } 8 | 9 | fn approx_eq_size(s1: Size, s2: Size) -> bool { 10 | approx_eq(s1.width, s2.width) && approx_eq(s1.height, s2.height) 11 | } 12 | 13 | fn approx_eq(f1: f64, f2: f64) -> bool { 14 | (f1 - f2).abs() < 1.0 15 | } 16 | 17 | mod window; 18 | pub use window::*; 19 | 20 | mod view; 21 | pub use view::*; 22 | 23 | mod button; 24 | pub use button::*; 25 | 26 | mod edit; 27 | pub use edit::*; 28 | 29 | mod text_box; 30 | pub use text_box::*; 31 | 32 | mod label; 33 | pub use label::*; 34 | 35 | mod canvas; 36 | pub use canvas::*; 37 | 38 | mod progress; 39 | pub use progress::*; 40 | 41 | mod combo_box; 42 | pub use combo_box::*; 43 | 44 | mod list_box; 45 | pub use list_box::*; 46 | 47 | mod check_box; 48 | pub use check_box::*; 49 | 50 | mod radio_button; 51 | pub use radio_button::*; 52 | 53 | mod scroll_bar; 54 | pub use scroll_bar::*; 55 | 56 | mod scroll_view; 57 | pub use scroll_view::*; 58 | 59 | mod slider; 60 | pub use slider::*; 61 | 62 | #[cfg(feature = "media")] 63 | mod media; 64 | #[cfg(feature = "media")] 65 | pub use media::*; 66 | 67 | #[cfg(feature = "webview")] 68 | mod webview; 69 | #[cfg(feature = "webview")] 70 | pub use webview::*; 71 | 72 | mod tab_view; 73 | pub use tab_view::*; 74 | -------------------------------------------------------------------------------- /winio-ui-gtk/src/ui/mod.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | 3 | use winio_primitive::ColorTheme; 4 | 5 | thread_local! { 6 | pub(crate) static COLOR_THEME: Cell> = const { Cell::new(None) }; 7 | } 8 | 9 | pub fn color_theme() -> crate::Result { 10 | COLOR_THEME.get().ok_or(crate::Error::NoColorTheme) 11 | } 12 | 13 | mod window; 14 | pub use window::*; 15 | 16 | mod canvas; 17 | pub use canvas::*; 18 | 19 | mod widget; 20 | pub(crate) use widget::*; 21 | 22 | mod monitor; 23 | pub use monitor::*; 24 | 25 | mod msgbox; 26 | pub use msgbox::*; 27 | 28 | mod filebox; 29 | pub use filebox::*; 30 | 31 | mod button; 32 | pub use button::*; 33 | 34 | mod edit; 35 | pub use edit::*; 36 | 37 | mod text_box; 38 | pub use text_box::*; 39 | 40 | mod label; 41 | pub use label::*; 42 | 43 | mod progress; 44 | pub use progress::*; 45 | 46 | mod combo_box; 47 | pub use combo_box::*; 48 | 49 | mod list_box; 50 | pub use list_box::*; 51 | 52 | mod check_box; 53 | pub use check_box::*; 54 | 55 | mod scroll_bar; 56 | pub use scroll_bar::*; 57 | 58 | mod scroll_view; 59 | pub use scroll_view::*; 60 | 61 | mod slider; 62 | pub use slider::*; 63 | 64 | #[cfg(feature = "media")] 65 | mod media; 66 | #[cfg(feature = "media")] 67 | pub use media::*; 68 | 69 | #[cfg(feature = "webview")] 70 | mod webview; 71 | #[cfg(feature = "webview")] 72 | pub use webview::*; 73 | 74 | mod accent; 75 | pub use accent::*; 76 | 77 | mod tab_view; 78 | pub use tab_view::*; 79 | -------------------------------------------------------------------------------- /winio/src/stub/ui/label.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{HAlign, Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct Label { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl Label { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn loc(&self) -> Result; 29 | 30 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 31 | 32 | pub fn size(&self) -> Result; 33 | 34 | pub fn set_size(&mut self, v: Size) -> Result<()>; 35 | 36 | pub fn tooltip(&self) -> Result; 37 | 38 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 39 | 40 | pub fn text(&self) -> Result; 41 | 42 | pub fn set_text(&mut self, s: impl AsRef) -> Result<()>; 43 | 44 | pub fn halign(&self) -> Result { 45 | not_impl() 46 | } 47 | 48 | pub fn set_halign(&mut self, _align: HAlign) -> Result<()> { 49 | not_impl() 50 | } 51 | } 52 | 53 | winio_handle::impl_as_widget!(Label, handle); 54 | -------------------------------------------------------------------------------- /winio/src/stub/ui/msgbox.rs: -------------------------------------------------------------------------------- 1 | use winio_handle::MaybeBorrowedWindow; 2 | use winio_primitive::{MessageBoxButton, MessageBoxResponse, MessageBoxStyle}; 3 | 4 | use crate::stub::{Result, not_impl}; 5 | 6 | #[derive(Debug, Default, Clone)] 7 | pub struct MessageBox; 8 | 9 | impl MessageBox { 10 | pub fn new() -> Self { 11 | not_impl() 12 | } 13 | 14 | pub async fn show( 15 | self, 16 | _parent: impl Into>, 17 | ) -> Result { 18 | not_impl() 19 | } 20 | 21 | pub fn message(&mut self, _msg: impl AsRef) { 22 | not_impl() 23 | } 24 | 25 | pub fn title(&mut self, _title: impl AsRef) { 26 | not_impl() 27 | } 28 | 29 | pub fn instruction(&mut self, _instr: impl AsRef) { 30 | not_impl() 31 | } 32 | 33 | pub fn style(&mut self, _style: MessageBoxStyle) { 34 | not_impl() 35 | } 36 | 37 | pub fn buttons(&mut self, _btns: MessageBoxButton) { 38 | not_impl() 39 | } 40 | 41 | pub fn custom_button(&mut self, _btn: impl Into) { 42 | not_impl() 43 | } 44 | 45 | pub fn custom_buttons(&mut self, _btns: impl IntoIterator>) { 46 | not_impl() 47 | } 48 | } 49 | 50 | #[derive(Debug, PartialEq, Eq, Clone)] 51 | pub struct CustomButton; 52 | 53 | impl CustomButton { 54 | pub fn new(_result: u16, _label: impl AsRef) -> Self { 55 | not_impl() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /winio/src/stub/ui/scroll_view.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct ScrollView { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl ScrollView { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn loc(&self) -> Result; 27 | 28 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 29 | 30 | pub fn size(&self) -> Result; 31 | 32 | pub fn set_size(&mut self, v: Size) -> Result<()>; 33 | 34 | pub fn hscroll(&self) -> Result { 35 | not_impl() 36 | } 37 | 38 | pub fn set_hscroll(&mut self, _v: bool) -> Result<()> { 39 | not_impl() 40 | } 41 | 42 | pub fn vscroll(&self) -> Result { 43 | not_impl() 44 | } 45 | 46 | pub fn set_vscroll(&mut self, _v: bool) -> Result<()> { 47 | not_impl() 48 | } 49 | 50 | pub async fn start(&self) -> ! { 51 | not_impl() 52 | } 53 | } 54 | 55 | winio_handle::impl_as_widget!(ScrollView, handle); 56 | winio_handle::impl_as_container!(ScrollView, handle); 57 | -------------------------------------------------------------------------------- /winio/src/stub/ui/check_box.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct CheckBox { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl CheckBox { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn loc(&self) -> Result; 29 | 30 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 31 | 32 | pub fn size(&self) -> Result; 33 | 34 | pub fn set_size(&mut self, v: Size) -> Result<()>; 35 | 36 | pub fn tooltip(&self) -> Result; 37 | 38 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 39 | 40 | pub fn text(&self) -> Result; 41 | 42 | pub fn set_text(&mut self, s: impl AsRef) -> Result<()>; 43 | 44 | pub fn is_checked(&self) -> Result { 45 | not_impl() 46 | } 47 | 48 | pub fn set_checked(&mut self, _v: bool) -> Result<()> { 49 | not_impl() 50 | } 51 | 52 | pub async fn wait_click(&self) { 53 | not_impl() 54 | } 55 | } 56 | 57 | winio_handle::impl_as_widget!(CheckBox, handle); 58 | -------------------------------------------------------------------------------- /winio/src/stub/ui/radio_button.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct RadioButton { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl RadioButton { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn loc(&self) -> Result; 29 | 30 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 31 | 32 | pub fn size(&self) -> Result; 33 | 34 | pub fn set_size(&mut self, v: Size) -> Result<()>; 35 | 36 | pub fn tooltip(&self) -> Result; 37 | 38 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 39 | 40 | pub fn text(&self) -> Result; 41 | 42 | pub fn set_text(&mut self, s: impl AsRef) -> Result<()>; 43 | 44 | pub fn is_checked(&self) -> Result { 45 | not_impl() 46 | } 47 | 48 | pub fn set_checked(&mut self, _v: bool) -> Result<()> { 49 | not_impl() 50 | } 51 | 52 | pub async fn wait_click(&self) { 53 | not_impl() 54 | } 55 | } 56 | 57 | winio_handle::impl_as_widget!(RadioButton, handle); 58 | -------------------------------------------------------------------------------- /winio-ui-gtk/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! GTK backend for winio. 2 | 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![cfg(not(any(windows, target_os = "macos")))] 5 | 6 | use winio_callback::Runnable; 7 | 8 | pub(crate) struct GlobalRuntime; 9 | 10 | impl Runnable for GlobalRuntime { 11 | #[inline] 12 | fn run() { 13 | RUNTIME.with(|runtime| runtime.run()) 14 | } 15 | } 16 | 17 | scoped_tls::scoped_thread_local!(pub(crate) static RUNTIME: Runtime); 18 | 19 | mod runtime; 20 | pub use runtime::*; 21 | 22 | mod ui; 23 | pub use ui::*; 24 | 25 | #[derive(Debug, thiserror::Error)] 26 | #[non_exhaustive] 27 | pub enum Error { 28 | /// IO error. 29 | #[error("IO error: {0}")] 30 | Io(#[from] std::io::Error), 31 | /// Bool error. 32 | #[error("Bool error: {0}")] 33 | Bool(#[from] gtk4::glib::BoolError), 34 | /// Glib error. 35 | #[error("Glib error: {0}")] 36 | Glib(#[from] gtk4::glib::Error), 37 | /// Cairo error. 38 | #[error("Cairo error: {0}")] 39 | Cairo(#[from] gtk4::cairo::Error), 40 | /// Index error. 41 | #[error("Index error: {0}")] 42 | Index(usize), 43 | /// Null pointer returned. 44 | #[error("Null pointer returned")] 45 | NullPointer, 46 | /// Cast failed. 47 | #[error("Cast failed")] 48 | CastFailed, 49 | /// Color theme is not available. 50 | #[error("Color theme is not available")] 51 | NoColorTheme, 52 | /// Feature not supported. 53 | #[error("Feature not supported")] 54 | NotSupported, 55 | } 56 | 57 | /// Result type for GTK. 58 | pub type Result = std::result::Result; 59 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/media.cpp: -------------------------------------------------------------------------------- 1 | #include "media.hpp" 2 | 3 | std::unique_ptr new_video(QWidget *parent) { 4 | auto w = std::make_unique(parent); 5 | w->setAspectRatioMode(Qt::KeepAspectRatio); 6 | return w; 7 | } 8 | 9 | std::unique_ptr new_player() { 10 | return std::make_unique(); 11 | } 12 | 13 | void player_connect_notify(WinioMediaPlayer &p, 14 | callback_fn_t callback, 15 | std::uint8_t const *data) { 16 | QObject::connect(&p, &QMediaPlayer::mediaStatusChanged, 17 | [&p, callback, data](QMediaPlayer::MediaStatus status) { 18 | switch (status) { 19 | case QMediaPlayer::LoadedMedia: 20 | callback(data, true); 21 | break; 22 | case QMediaPlayer::InvalidMedia: 23 | callback(data, false); 24 | break; 25 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 26 | case QMediaPlayer::EndOfMedia: 27 | // In Qt 5, we manually handle infinite looping. 28 | if (p.loops() < 0) { 29 | p.setPosition(0); 30 | p.play(); 31 | } 32 | break; 33 | #endif 34 | default: 35 | break; 36 | } 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /winio-ui-win32/src/ui/dpi.rs: -------------------------------------------------------------------------------- 1 | use windows_sys::Win32::{ 2 | Foundation::HWND, 3 | System::WindowsProgramming::MulDiv, 4 | UI::{ 5 | HiDpi::{GetDpiForSystem, GetDpiForWindow}, 6 | WindowsAndMessaging::USER_DEFAULT_SCREEN_DPI, 7 | }, 8 | }; 9 | use winio_primitive::{Point, Rect, Size}; 10 | 11 | #[inline] 12 | pub fn get_dpi_for_window(h_wnd: HWND) -> u32 { 13 | if !h_wnd.is_null() { 14 | unsafe { GetDpiForWindow(h_wnd) } 15 | } else { 16 | unsafe { GetDpiForSystem() } 17 | } 18 | } 19 | 20 | pub trait DpiAware { 21 | fn to_logical(self, dpi: u32) -> Self; 22 | fn to_device(self, dpi: u32) -> Self; 23 | } 24 | 25 | impl DpiAware for i32 { 26 | #[inline] 27 | fn to_logical(self, dpi: u32) -> Self { 28 | unsafe { MulDiv(self, USER_DEFAULT_SCREEN_DPI as _, dpi as i32) } 29 | } 30 | 31 | #[inline] 32 | fn to_device(self, dpi: u32) -> Self { 33 | unsafe { MulDiv(self, dpi as i32, USER_DEFAULT_SCREEN_DPI as _) } 34 | } 35 | } 36 | 37 | macro_rules! impl_dpi_aware_for_f64 { 38 | ($t:ty) => { 39 | impl DpiAware for $t { 40 | #[inline] 41 | fn to_logical(self, dpi: u32) -> Self { 42 | self / (dpi as f64) * (USER_DEFAULT_SCREEN_DPI as f64) 43 | } 44 | 45 | #[inline] 46 | fn to_device(self, dpi: u32) -> Self { 47 | self / (USER_DEFAULT_SCREEN_DPI as f64) * (dpi as f64) 48 | } 49 | } 50 | }; 51 | } 52 | 53 | impl_dpi_aware_for_f64!(f64); 54 | impl_dpi_aware_for_f64!(Point); 55 | impl_dpi_aware_for_f64!(Size); 56 | impl_dpi_aware_for_f64!(Rect); 57 | -------------------------------------------------------------------------------- /winio/src/widgets/view.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_elm::{Component, ComponentSender}; 3 | use winio_handle::BorrowedContainer; 4 | use winio_primitive::{Failable, Layoutable, Point, Size, Visible}; 5 | 6 | use crate::{ 7 | sys, 8 | sys::{Error, Result}, 9 | }; 10 | 11 | /// A simple window. 12 | #[derive(Debug)] 13 | pub struct View { 14 | widget: sys::View, 15 | } 16 | 17 | impl Failable for View { 18 | type Error = Error; 19 | } 20 | 21 | #[inherit_methods(from = "self.widget")] 22 | impl Visible for View { 23 | fn is_visible(&self) -> Result; 24 | 25 | fn set_visible(&mut self, v: bool) -> Result<()>; 26 | } 27 | 28 | #[inherit_methods(from = "self.widget")] 29 | impl Layoutable for View { 30 | fn loc(&self) -> Result; 31 | 32 | fn set_loc(&mut self, p: Point) -> Result<()>; 33 | 34 | fn size(&self) -> Result; 35 | 36 | fn set_size(&mut self, v: Size) -> Result<()>; 37 | } 38 | 39 | /// Events of [`View`]. 40 | #[derive(Debug)] 41 | #[non_exhaustive] 42 | pub enum ViewEvent {} 43 | 44 | /// Messages of [`View`]. 45 | #[derive(Debug)] 46 | #[non_exhaustive] 47 | pub enum ViewMessage {} 48 | 49 | impl Component for View { 50 | type Error = Error; 51 | type Event = ViewEvent; 52 | type Init<'a> = BorrowedContainer<'a>; 53 | type Message = ViewMessage; 54 | 55 | async fn init(init: Self::Init<'_>, _sender: &ComponentSender) -> Result { 56 | let widget = sys::View::new(init)?; 57 | Ok(Self { widget }) 58 | } 59 | } 60 | 61 | winio_handle::impl_as_widget!(View, widget); 62 | winio_handle::impl_as_container!(View, widget); 63 | -------------------------------------------------------------------------------- /winio-primitive/src/monitor.rs: -------------------------------------------------------------------------------- 1 | use crate::{Point, Rect, Size}; 2 | 3 | /// Represents the geometry of a monitor. 4 | #[derive(Debug, Clone, PartialEq)] 5 | pub struct Monitor { 6 | region: Rect, 7 | client: Rect, 8 | dpi: Size, 9 | } 10 | 11 | impl Monitor { 12 | /// Create a monitor geometry. 13 | pub fn new(region: Rect, client: Rect, dpi: Size) -> Self { 14 | Self { 15 | region, 16 | client, 17 | dpi, 18 | } 19 | } 20 | 21 | /// The physical region. 22 | pub fn region(&self) -> Rect { 23 | self.region 24 | } 25 | 26 | /// The client region. 27 | pub fn client(&self) -> Rect { 28 | self.client 29 | } 30 | 31 | /// Dpi of the monitor, 1.0 if no scale. You should take it into 32 | /// consideration when setting the location of windows. 33 | /// See [`Monitor::region_scaled`] & [`Monitor::client_scaled`]. 34 | pub fn dpi(&self) -> Size { 35 | self.dpi 36 | } 37 | 38 | /// Scaled physical region. 39 | pub fn region_scaled(&self) -> Rect { 40 | div_rect(self.region, self.dpi) 41 | } 42 | 43 | /// Scaled client region. 44 | pub fn client_scaled(&self) -> Rect { 45 | div_rect(self.client, self.dpi) 46 | } 47 | } 48 | 49 | #[inline] 50 | fn div_rect(r: Rect, s: Size) -> Rect { 51 | Rect::new(div_point(r.origin, s), div_size(r.size, s)) 52 | } 53 | 54 | #[inline] 55 | fn div_point(p: Point, s: Size) -> Point { 56 | Point::new(p.x / s.width, p.y / s.height) 57 | } 58 | 59 | #[inline] 60 | fn div_size(s1: Size, s2: Size) -> Size { 61 | Size::new(s1.width / s2.width, s1.height / s2.height) 62 | } 63 | -------------------------------------------------------------------------------- /winio-ui-app-kit/src/ui/monitor.rs: -------------------------------------------------------------------------------- 1 | use objc2::{MainThreadMarker, rc::Retained}; 2 | use objc2_app_kit::{NSDeviceResolution, NSScreen}; 3 | use objc2_foundation::NSValue; 4 | use winio_primitive::{Monitor, Point, Rect, Size}; 5 | 6 | use crate::{ 7 | Error, Result, catch, 8 | ui::{from_cgsize, transform_cgrect}, 9 | }; 10 | 11 | pub fn monitor_get_all() -> Result> { 12 | let mtm = MainThreadMarker::new().ok_or(Error::NotMainThread)?; 13 | let mut res = vec![]; 14 | catch(|| { 15 | for screen in NSScreen::screens(mtm) { 16 | let frame = screen.frame(); 17 | let vframe = screen.visibleFrame(); 18 | 19 | let frame_size = from_cgsize(frame.size); 20 | let frame = transform_cgrect(frame_size, frame); 21 | let vframe = transform_cgrect(frame_size, vframe); 22 | 23 | let dpi = screen 24 | .deviceDescription() 25 | .objectForKey(unsafe { NSDeviceResolution }) 26 | .map(|obj| { 27 | from_cgsize(unsafe { Retained::cast_unchecked::(obj).sizeValue() }) 28 | }) 29 | .unwrap_or(Size::new(1.0, 1.0)); 30 | 31 | res.push(Monitor::new( 32 | rect_scale(frame, dpi), 33 | rect_scale(vframe, dpi), 34 | dpi, 35 | )) 36 | } 37 | })?; 38 | Ok(res) 39 | } 40 | 41 | #[inline] 42 | fn rect_scale(r: Rect, dpi: Size) -> Rect { 43 | Rect::new( 44 | Point::new(r.origin.x * dpi.width, r.origin.y * dpi.height), 45 | Size::new(r.size.width * dpi.width, r.size.height * dpi.height), 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /winio/src/stub/ui/filebox.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use winio_handle::MaybeBorrowedWindow; 4 | 5 | use crate::stub::{Result, not_impl}; 6 | 7 | #[derive(Debug, Default, Clone)] 8 | pub struct FileBox; 9 | 10 | impl FileBox { 11 | pub fn new() -> Self { 12 | not_impl() 13 | } 14 | 15 | pub fn title(&mut self, _title: impl AsRef) { 16 | not_impl() 17 | } 18 | 19 | pub fn filename(&mut self, _filename: impl AsRef) { 20 | not_impl() 21 | } 22 | 23 | pub fn filters(&mut self, _filters: impl IntoIterator) { 24 | not_impl() 25 | } 26 | 27 | pub fn add_filter(&mut self, _filter: impl Into) { 28 | not_impl() 29 | } 30 | 31 | pub async fn open( 32 | self, 33 | _parent: impl Into>, 34 | ) -> Result> { 35 | not_impl() 36 | } 37 | 38 | pub async fn open_multiple( 39 | self, 40 | _parent: impl Into>, 41 | ) -> Result> { 42 | not_impl() 43 | } 44 | 45 | pub async fn open_folder( 46 | self, 47 | _parent: impl Into>, 48 | ) -> Result> { 49 | not_impl() 50 | } 51 | 52 | pub async fn save( 53 | self, 54 | _parent: impl Into>, 55 | ) -> Result> { 56 | not_impl() 57 | } 58 | } 59 | 60 | #[derive(Debug, Clone, PartialEq, Eq)] 61 | pub struct FileFilter; 62 | 63 | impl FileFilter { 64 | pub fn new(_name: impl AsRef, _pattern: impl AsRef) -> Self { 65 | not_impl() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct WinioMainWindow : public QMainWindow { 12 | callback_t m_resize_callback; 13 | callback_t m_move_callback; 14 | callback_t m_close_callback; 15 | callback_t m_theme_callback; 16 | 17 | WinioMainWindow(QWidget *parent); 18 | ~WinioMainWindow() override; 19 | 20 | protected: 21 | void resizeEvent(QResizeEvent *event) override; 22 | void moveEvent(QMoveEvent *event) override; 23 | void closeEvent(QCloseEvent *event) override; 24 | void changeEvent(QEvent *event) override; 25 | }; 26 | 27 | std::unique_ptr new_main_window(); 28 | 29 | STATIC_CAST_ASSERT(QMainWindow, QWidget); 30 | 31 | void main_window_register_resize_event(QMainWindow &w, 32 | callback_fn_t callback, 33 | std::uint8_t const *data); 34 | void main_window_register_move_event(QMainWindow &w, 35 | callback_fn_t callback, 36 | std::uint8_t const *data); 37 | void main_window_register_close_event(QMainWindow &w, 38 | callback_fn_t callback, 39 | std::uint8_t const *data); 40 | void main_window_register_theme_event(QMainWindow &w, 41 | callback_fn_t callback, 42 | std::uint8_t const *data); 43 | -------------------------------------------------------------------------------- /winio-ui-app-kit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-ui-app-kit" 3 | version = "0.4.0" 4 | description = "AppKit backend for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [target.'cfg(target_os = "macos")'.dependencies] 12 | winio-primitive = { workspace = true } 13 | winio-handle = { workspace = true } 14 | winio-callback = { workspace = true } 15 | winio-pollable = { workspace = true } 16 | 17 | compio = { workspace = true, features = ["arrayvec"] } 18 | compio-log = { workspace = true } 19 | 20 | inherit-methods-macro = { workspace = true } 21 | image = { workspace = true } 22 | local-sync = { workspace = true } 23 | scoped-tls = { workspace = true } 24 | thiserror = { workspace = true } 25 | 26 | block2 = "0.6" 27 | objc2 = { workspace = true, features = ["exception"] } 28 | objc2-core-foundation = "0.3" 29 | objc2-core-graphics = "0.3" 30 | objc2-core-text = "0.3" 31 | objc2-foundation = "0.3" 32 | objc2-app-kit = { workspace = true, features = [ 33 | "objc2-uniform-type-identifiers", 34 | ] } 35 | objc2-uniform-type-identifiers = "0.3" 36 | objc2-av-kit = { version = "0.3", optional = true } 37 | objc2-av-foundation = { version = "0.3", features = [ 38 | "objc2-core-media", 39 | ], optional = true } 40 | objc2-core-media = { version = "0.3", optional = true } 41 | objc2-web-kit = { version = "0.3", optional = true } 42 | 43 | [features] 44 | media = ["dep:objc2-av-kit", "dep:objc2-av-foundation", "dep:objc2-core-media"] 45 | webview = ["dep:objc2-web-kit"] 46 | 47 | objc-static = [ 48 | "objc2/unstable-static-class-inlined", 49 | "objc2/unstable-static-sel-inlined", 50 | "objc2-foundation/unstable-static-nsstring", 51 | ] 52 | -------------------------------------------------------------------------------- /winio/src/stub/ui/text_box.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{HAlign, Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct TextBox { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl TextBox { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn min_size(&self) -> Result; 29 | 30 | pub fn loc(&self) -> Result; 31 | 32 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 33 | 34 | pub fn size(&self) -> Result; 35 | 36 | pub fn set_size(&mut self, v: Size) -> Result<()>; 37 | 38 | pub fn tooltip(&self) -> Result; 39 | 40 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 41 | 42 | pub fn text(&self) -> Result; 43 | 44 | pub fn set_text(&mut self, s: impl AsRef) -> Result<()>; 45 | 46 | pub fn halign(&self) -> Result { 47 | not_impl() 48 | } 49 | 50 | pub fn set_halign(&mut self, _align: HAlign) -> Result<()> { 51 | not_impl() 52 | } 53 | 54 | pub fn is_readonly(&self) -> Result { 55 | not_impl() 56 | } 57 | 58 | pub fn set_readonly(&mut self, _v: bool) -> Result<()> { 59 | not_impl() 60 | } 61 | 62 | pub async fn wait_change(&self) { 63 | not_impl() 64 | } 65 | } 66 | 67 | winio_handle::impl_as_widget!(TextBox, handle); 68 | -------------------------------------------------------------------------------- /winio-ui-gtk/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, os::fd::AsRawFd}; 2 | 3 | use gtk4::{ 4 | gio::{self, prelude::ApplicationExt}, 5 | glib::{ControlFlow, IOCondition, MainContext, timeout_add_local_once, unix_fd_add_local}, 6 | }; 7 | 8 | use crate::Result; 9 | 10 | pub struct Runtime { 11 | runtime: winio_pollable::Runtime, 12 | app: gio::Application, 13 | ctx: MainContext, 14 | } 15 | 16 | impl Runtime { 17 | pub fn new() -> Result { 18 | let runtime = winio_pollable::Runtime::new()?; 19 | let poll_fd = runtime.as_raw_fd(); 20 | let ctx = MainContext::default(); 21 | gtk4::init()?; 22 | let app = gio::Application::new(None, gio::ApplicationFlags::FLAGS_NONE); 23 | app.set_default(); 24 | 25 | unix_fd_add_local(poll_fd, IOCondition::IN, |_fd, _cond| ControlFlow::Continue); 26 | 27 | Ok(Self { runtime, app, ctx }) 28 | } 29 | 30 | pub fn set_app_id(&mut self, name: &str) -> Result<()> { 31 | self.app.set_application_id(Some(name)); 32 | Ok(()) 33 | } 34 | 35 | pub(crate) fn run(&self) { 36 | self.runtime.run(); 37 | } 38 | 39 | fn enter T>(&self, f: F) -> T { 40 | self.runtime.enter(|| super::RUNTIME.set(self, f)) 41 | } 42 | 43 | pub fn block_on(&self, future: F) -> F::Output { 44 | self.enter(|| { 45 | self.runtime.block_on(future, |timeout| { 46 | let source_id = timeout.map(|timeout| timeout_add_local_once(timeout, || {})); 47 | 48 | self.ctx.iteration(true); 49 | 50 | if let Some(source_id) = source_id 51 | && self.ctx.find_source_by_id(&source_id).is_some() 52 | { 53 | source_id.remove(); 54 | } 55 | }) 56 | }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /winio/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A single-threaded asynchronous GUI runtime, based on [`compio`] and ELM 2 | //! design. 3 | 4 | #![cfg_attr(docsrs, feature(doc_cfg))] 5 | #![warn(missing_docs)] 6 | 7 | #[doc(no_inline)] 8 | pub use compio; 9 | #[doc(inline)] 10 | pub use winio_elm as elm; 11 | #[doc(inline)] 12 | pub use winio_handle as handle; 13 | #[doc(inline)] 14 | pub use winio_layout as layout; 15 | #[doc(inline)] 16 | pub use winio_primitive as primitive; 17 | 18 | cfg_if::cfg_if! { 19 | if #[cfg(windows)] { 20 | #[cfg(all(feature = "win32", feature = "winui"))] 21 | compile_error!("You must choose only one of these features: [\"win32\", \"winui\"]"); 22 | 23 | cfg_if::cfg_if! { 24 | if #[cfg(feature = "winui")] { 25 | use winio_ui_winui as sys; 26 | } else if #[cfg(feature = "win32")] { 27 | use winio_ui_win32 as sys; 28 | } else { 29 | mod stub; 30 | use stub as sys; 31 | } 32 | } 33 | } else if #[cfg(target_os = "macos")] { 34 | use winio_ui_app_kit as sys; 35 | } else { 36 | #[cfg(all(feature = "gtk", feature = "qt"))] 37 | compile_error!("You must choose only one of these features: [\"gtk\", \"qt\"]"); 38 | 39 | cfg_if::cfg_if! { 40 | if #[cfg(feature = "qt")] { 41 | use winio_ui_qt as sys; 42 | } else if #[cfg(feature = "gtk")] { 43 | use winio_ui_gtk as sys; 44 | } else { 45 | mod stub; 46 | use stub as sys; 47 | } 48 | } 49 | } 50 | } 51 | 52 | pub mod ui; 53 | 54 | pub mod widgets; 55 | 56 | /// For blanket imports. 57 | pub mod prelude { 58 | pub use crate::{Error, Result, elm::*, handle::*, layout::*, primitive::*, ui::*, widgets::*}; 59 | } 60 | 61 | pub use sys::{Error, Result}; 62 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/media.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | #include 6 | 7 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 8 | #include 9 | #endif 10 | 11 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 12 | struct WinioMediaPlayer : public QMediaPlayer { 13 | private: 14 | QAudioOutput m_audio; 15 | 16 | public: 17 | WinioMediaPlayer() : QMediaPlayer(), m_audio() { setAudioOutput(&m_audio); } 18 | ~WinioMediaPlayer() override { setAudioOutput(nullptr); } 19 | 20 | double volume() const { return m_audio.volume(); } 21 | void setVolume(double v) { m_audio.setVolume(v); } 22 | 23 | bool isMuted() const { return m_audio.isMuted(); } 24 | void setMuted(bool v) { m_audio.setMuted(v); } 25 | 26 | void setVideoOutput(QVideoWidget *w) { QMediaPlayer::setVideoOutput(w); } 27 | }; 28 | #else 29 | struct WinioMediaPlayer : public QMediaPlayer { 30 | private: 31 | int m_loops = 1; 32 | 33 | public: 34 | WinioMediaPlayer() : QMediaPlayer() {} 35 | ~WinioMediaPlayer() override {} 36 | 37 | double volume() const { return ((double)QMediaPlayer::volume()) / 100.0; } 38 | void setVolume(double v) { QMediaPlayer::setVolume(v * 100.0); } 39 | 40 | QUrl source() const { return media().canonicalUrl(); } 41 | void setSource(const QUrl &url) { setMedia(url); } 42 | 43 | int loops() const { return m_loops; } 44 | void setLoops(int loops) { m_loops = loops; } 45 | }; 46 | #endif 47 | 48 | STATIC_CAST_ASSERT(QVideoWidget, QWidget); 49 | 50 | using QMediaPlayerError = QMediaPlayer::Error; 51 | 52 | std::unique_ptr new_video(QWidget *parent); 53 | std::unique_ptr new_player(); 54 | 55 | void player_connect_notify(WinioMediaPlayer &p, 56 | callback_fn_t callback, 57 | std::uint8_t const *data); 58 | -------------------------------------------------------------------------------- /winio-elm/src/channel.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::Debug, 3 | future::Future, 4 | pin::Pin, 5 | sync::{Arc, Mutex}, 6 | task::{Context, Poll, Waker}, 7 | }; 8 | 9 | use smallvec::SmallVec; 10 | 11 | struct ChannelInner { 12 | data: SmallVec<[T; 1]>, 13 | waker: Option, 14 | } 15 | 16 | pub struct Channel(Arc>>); 17 | 18 | impl Channel { 19 | pub fn new() -> Self { 20 | Self(Arc::new(Mutex::new(ChannelInner { 21 | data: SmallVec::new(), 22 | waker: None, 23 | }))) 24 | } 25 | 26 | pub fn send(&self, data: T) { 27 | let mut inner = self.0.lock().unwrap(); 28 | inner.data.push(data); 29 | if let Some(waker) = inner.waker.take() { 30 | waker.wake(); 31 | } 32 | } 33 | 34 | pub fn wait(&self) -> impl Future + '_ { 35 | RecvFut(&self.0) 36 | } 37 | 38 | pub fn fetch_all(&self) -> SmallVec<[T; 1]> { 39 | let mut inner = self.0.lock().unwrap(); 40 | std::mem::take(&mut inner.data) 41 | } 42 | } 43 | 44 | impl Debug for Channel { 45 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 46 | f.debug_tuple("Channel").finish_non_exhaustive() 47 | } 48 | } 49 | 50 | impl Clone for Channel { 51 | fn clone(&self) -> Self { 52 | Self(self.0.clone()) 53 | } 54 | } 55 | 56 | struct RecvFut<'a, T>(&'a Mutex>); 57 | 58 | impl Future for RecvFut<'_, T> { 59 | type Output = (); 60 | 61 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 62 | let mut inner = self.0.lock().unwrap(); 63 | if inner.data.is_empty() { 64 | inner.waker = Some(cx.waker().clone()); 65 | Poll::Pending 66 | } else { 67 | Poll::Ready(()) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /winio-ui-qt/src/ui/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template struct callback_traits; 10 | 11 | template struct callback_traits { 12 | using fn_type = rust::Fn; 13 | using type = std::optional>; 14 | }; 15 | 16 | template using callback_t = typename callback_traits::type; 17 | template 18 | using callback_fn_t = typename callback_traits::fn_type; 19 | 20 | namespace rust { 21 | template <> struct IsRelocatable : std::true_type {}; 22 | } // namespace rust 23 | 24 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 25 | static_assert(sizeof(QString) == 3 * sizeof(std::size_t)); 26 | #else 27 | static_assert(sizeof(QString) == sizeof(std::size_t)); 28 | #endif 29 | 30 | #define STATIC_CAST_ASSERT(t, base) \ 31 | static_assert(std::is_base_of::value && \ 32 | std::is_convertible::value && \ 33 | std::is_polymorphic::value && \ 34 | std::is_polymorphic::value); 35 | 36 | inline QString new_string_utf8(const std::uint8_t *p, std::size_t len) { 37 | return QString::fromUtf8((const char *)p, (qsizetype)len); 38 | } 39 | 40 | inline std::size_t string_len(const QString &s) noexcept { return s.size(); } 41 | 42 | inline void string_drop(QString &s) noexcept { s.~QString(); } 43 | 44 | namespace rust { 45 | template <> struct IsRelocatable : std::true_type {}; 46 | } // namespace rust 47 | 48 | inline QUrl new_url(const QString &s) { return QUrl{s}; } 49 | 50 | inline QString url_to_qstring(const QUrl &url) { return url.toString(); } 51 | -------------------------------------------------------------------------------- /winio-ui-qt/src/runtime/qt.cpp: -------------------------------------------------------------------------------- 1 | #include "qt.hpp" 2 | 3 | #include 4 | #include 5 | 6 | std::unique_ptr new_event_loop(rust::Vec args, 7 | int fd) { 8 | return std::make_unique(args, fd); 9 | } 10 | 11 | static std::vector args_ptr(rust::Vec const &args) { 12 | auto result = std::vector{}; 13 | for (auto &arg : args) { 14 | result.push_back(arg.data()); 15 | } 16 | return result; 17 | } 18 | 19 | WinioQtEventLoop::WinioQtEventLoop(rust::Vec args, int fd) 20 | : m_args(std::move(args)), m_args_ptr(args_ptr(m_args)), 21 | m_argc(m_args.size()), m_app{m_argc, (char **)m_args_ptr.data()}, 22 | m_notifier{fd, QSocketNotifier::Read} { 23 | QApplication::setQuitOnLastWindowClosed(false); 24 | QObject::connect(&m_notifier, &QSocketNotifier::activated, []() {}); 25 | m_notifier.setEnabled(true); 26 | } 27 | 28 | void WinioQtEventLoop::process() { 29 | auto dispatcher = QApplication::eventDispatcher(); 30 | dispatcher->processEvents( 31 | #if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) 32 | QEventLoop::ApplicationExec | 33 | #endif 34 | QEventLoop::WaitForMoreEvents | QEventLoop::EventLoopExec); 35 | } 36 | 37 | void WinioQtEventLoop::process(int maxtime) { 38 | auto dispatcher = QApplication::eventDispatcher(); 39 | auto id = dispatcher->registerTimer(maxtime, Qt::CoarseTimer, qApp); 40 | dispatcher->processEvents( 41 | #if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) 42 | QEventLoop::ApplicationExec | 43 | #endif 44 | QEventLoop::WaitForMoreEvents | QEventLoop::EventLoopExec); 45 | dispatcher->unregisterTimer(id); 46 | } 47 | 48 | void WinioQtEventLoop::setAppName(rust::Str name) { 49 | m_app.setDesktopFileName(QString::fromUtf8(name.data(), name.size())); 50 | } 51 | -------------------------------------------------------------------------------- /winio/src/stub/ui/progress.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct Progress { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl Progress { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn loc(&self) -> Result; 29 | 30 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 31 | 32 | pub fn size(&self) -> Result; 33 | 34 | pub fn set_size(&mut self, v: Size) -> Result<()>; 35 | 36 | pub fn tooltip(&self) -> Result; 37 | 38 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 39 | 40 | pub fn minimum(&self) -> Result { 41 | not_impl() 42 | } 43 | 44 | pub fn set_minimum(&mut self, _v: usize) -> Result<()> { 45 | not_impl() 46 | } 47 | 48 | pub fn maximum(&self) -> Result { 49 | not_impl() 50 | } 51 | 52 | pub fn set_maximum(&mut self, _v: usize) -> Result<()> { 53 | not_impl() 54 | } 55 | 56 | pub fn pos(&self) -> Result { 57 | not_impl() 58 | } 59 | 60 | pub fn set_pos(&mut self, _pos: usize) -> Result<()> { 61 | not_impl() 62 | } 63 | 64 | pub fn is_indeterminate(&self) -> Result { 65 | not_impl() 66 | } 67 | 68 | pub fn set_indeterminate(&mut self, _v: bool) -> Result<()> { 69 | not_impl() 70 | } 71 | } 72 | 73 | winio_handle::impl_as_widget!(Progress, handle); 74 | -------------------------------------------------------------------------------- /winio/src/stub/ui/edit.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{HAlign, Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct Edit { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl Edit { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn loc(&self) -> Result; 29 | 30 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 31 | 32 | pub fn size(&self) -> Result; 33 | 34 | pub fn set_size(&mut self, v: Size) -> Result<()>; 35 | 36 | pub fn tooltip(&self) -> Result; 37 | 38 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 39 | 40 | pub fn text(&self) -> Result; 41 | 42 | pub fn set_text(&mut self, s: impl AsRef) -> Result<()>; 43 | 44 | pub fn is_password(&self) -> Result { 45 | not_impl() 46 | } 47 | 48 | pub fn set_password(&mut self, _v: bool) -> Result<()> { 49 | not_impl() 50 | } 51 | 52 | pub fn halign(&self) -> Result { 53 | not_impl() 54 | } 55 | 56 | pub fn set_halign(&mut self, _align: HAlign) -> Result<()> { 57 | not_impl() 58 | } 59 | 60 | pub fn is_readonly(&self) -> Result { 61 | not_impl() 62 | } 63 | 64 | pub fn set_readonly(&mut self, _v: bool) -> Result<()> { 65 | not_impl() 66 | } 67 | 68 | pub async fn wait_change(&self) { 69 | not_impl() 70 | } 71 | } 72 | 73 | winio_handle::impl_as_widget!(Edit, handle); 74 | -------------------------------------------------------------------------------- /winio/src/stub/ui/webview.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct WebView { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl WebView { 14 | pub async fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn loc(&self) -> Result; 27 | 28 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 29 | 30 | pub fn size(&self) -> Result; 31 | 32 | pub fn set_size(&mut self, v: Size) -> Result<()>; 33 | 34 | pub fn source(&self) -> Result { 35 | not_impl() 36 | } 37 | 38 | pub fn set_source(&mut self, _s: impl AsRef) -> Result<()> { 39 | not_impl() 40 | } 41 | 42 | pub fn set_html(&mut self, _s: impl AsRef) -> Result<()> { 43 | not_impl() 44 | } 45 | 46 | pub fn can_go_forward(&self) -> Result { 47 | not_impl() 48 | } 49 | 50 | pub fn go_forward(&mut self) -> Result<()> { 51 | not_impl() 52 | } 53 | 54 | pub fn can_go_back(&self) -> Result { 55 | not_impl() 56 | } 57 | 58 | pub fn go_back(&mut self) -> Result<()> { 59 | not_impl() 60 | } 61 | 62 | pub fn reload(&mut self) -> Result<()> { 63 | not_impl() 64 | } 65 | 66 | pub fn stop(&mut self) -> Result<()> { 67 | not_impl() 68 | } 69 | 70 | pub async fn wait_navigating(&self) { 71 | not_impl() 72 | } 73 | 74 | pub async fn wait_navigated(&self) { 75 | not_impl() 76 | } 77 | } 78 | 79 | winio_handle::impl_as_widget!(WebView, handle); 80 | -------------------------------------------------------------------------------- /winio/src/stub/ui/widget.rs: -------------------------------------------------------------------------------- 1 | use winio_handle::{ 2 | AsContainer, AsWidget, AsWindow, BorrowedContainer, BorrowedWidget, BorrowedWindow, 3 | }; 4 | use winio_primitive::{Point, Size}; 5 | 6 | use crate::stub::{Result, not_impl}; 7 | 8 | #[derive(Debug)] 9 | pub(crate) struct Widget; 10 | 11 | impl Widget { 12 | pub fn is_visible(&self) -> Result { 13 | not_impl() 14 | } 15 | 16 | pub fn set_visible(&mut self, _v: bool) -> Result<()> { 17 | not_impl() 18 | } 19 | 20 | pub fn is_enabled(&self) -> Result { 21 | not_impl() 22 | } 23 | 24 | pub fn set_enabled(&mut self, _v: bool) -> Result<()> { 25 | not_impl() 26 | } 27 | 28 | pub fn preferred_size(&self) -> Result { 29 | not_impl() 30 | } 31 | 32 | pub fn min_size(&self) -> Result { 33 | not_impl() 34 | } 35 | 36 | pub fn loc(&self) -> Result { 37 | not_impl() 38 | } 39 | 40 | pub fn set_loc(&mut self, _p: Point) -> Result<()> { 41 | not_impl() 42 | } 43 | 44 | pub fn size(&self) -> Result { 45 | not_impl() 46 | } 47 | 48 | pub fn set_size(&mut self, _v: Size) -> Result<()> { 49 | not_impl() 50 | } 51 | 52 | pub fn text(&self) -> Result { 53 | not_impl() 54 | } 55 | 56 | pub fn set_text(&mut self, _s: impl AsRef) -> Result<()> { 57 | not_impl() 58 | } 59 | 60 | pub fn tooltip(&self) -> Result { 61 | not_impl() 62 | } 63 | 64 | pub fn set_tooltip(&mut self, _s: impl AsRef) -> Result<()> { 65 | not_impl() 66 | } 67 | } 68 | 69 | impl AsWindow for Widget { 70 | fn as_window(&self) -> BorrowedWindow<'_> { 71 | not_impl() 72 | } 73 | } 74 | 75 | impl AsContainer for Widget { 76 | fn as_container(&self) -> BorrowedContainer<'_> { 77 | not_impl() 78 | } 79 | } 80 | 81 | impl AsWidget for Widget { 82 | fn as_widget(&self) -> BorrowedWidget<'_> { 83 | not_impl() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /winio-ui-winui/src/ui/label.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use windows::core::{HSTRING, Interface}; 3 | use winio_handle::AsContainer; 4 | use winio_primitive::{HAlign, Point, Size}; 5 | use winui3::Microsoft::UI::Xaml::{Controls as MUXC, TextWrapping}; 6 | 7 | use crate::{Result, Widget, ui::Convertible}; 8 | 9 | #[derive(Debug)] 10 | pub struct Label { 11 | handle: Widget, 12 | label: MUXC::TextBlock, 13 | } 14 | 15 | #[inherit_methods(from = "self.handle")] 16 | impl Label { 17 | pub fn new(parent: impl AsContainer) -> Result { 18 | let label = MUXC::TextBlock::new()?; 19 | label.SetTextWrapping(TextWrapping::Wrap)?; 20 | Ok(Self { 21 | handle: Widget::new(parent, label.cast()?)?, 22 | label, 23 | }) 24 | } 25 | 26 | pub fn is_visible(&self) -> Result; 27 | 28 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 29 | 30 | pub fn is_enabled(&self) -> Result; 31 | 32 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 33 | 34 | pub fn preferred_size(&self) -> Result; 35 | 36 | pub fn loc(&self) -> Result; 37 | 38 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 39 | 40 | pub fn size(&self) -> Result; 41 | 42 | pub fn set_size(&mut self, v: Size) -> Result<()>; 43 | 44 | pub fn tooltip(&self) -> Result; 45 | 46 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 47 | 48 | pub fn text(&self) -> Result { 49 | Ok(self.label.Text()?.to_string_lossy()) 50 | } 51 | 52 | pub fn set_text(&mut self, s: impl AsRef) -> Result<()> { 53 | self.label.SetText(&HSTRING::from(s.as_ref()))?; 54 | Ok(()) 55 | } 56 | 57 | pub fn halign(&self) -> Result { 58 | Ok(HAlign::from_native(self.label.TextAlignment()?)) 59 | } 60 | 61 | pub fn set_halign(&mut self, align: HAlign) -> Result<()> { 62 | self.label.SetTextAlignment(align.to_native())?; 63 | Ok(()) 64 | } 65 | } 66 | 67 | winio_handle::impl_as_widget!(Label, handle); 68 | -------------------------------------------------------------------------------- /winio-ui-windows-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-ui-windows-common" 3 | version = "0.5.0" 4 | description = "Windows common methods for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [target.'cfg(windows)'.dependencies] 12 | winio-primitive = { workspace = true } 13 | winio-handle = { workspace = true } 14 | winio-pollable = { workspace = true } 15 | 16 | compio = { workspace = true } 17 | compio-log = { workspace = true } 18 | 19 | cfg-if = { workspace = true } 20 | image = { workspace = true } 21 | once_cell = { workspace = true } 22 | widestring = { workspace = true } 23 | 24 | windows = { workspace = true, features = [ 25 | "UI_ViewManagement", 26 | "Win32_Graphics_Direct2D", 27 | "Win32_Graphics_Direct2D_Common", 28 | "Win32_Graphics_DirectWrite", 29 | "Win32_Graphics_Dxgi_Common", 30 | "Win32_System_Com", 31 | "Win32_UI_Shell", 32 | "Win32_UI_Shell_Common", 33 | ] } 34 | windows-sys = { workspace = true, features = [ 35 | "Win32_Foundation", 36 | "Win32_Globalization", 37 | "Win32_Graphics_Dwm", 38 | "Win32_Graphics_Gdi", 39 | "Win32_System_LibraryLoader", 40 | "Win32_System_SystemServices", 41 | "Win32_System_Threading", 42 | "Win32_UI", 43 | "Win32_UI_Accessibility", 44 | "Win32_UI_Controls", 45 | "Win32_UI_HiDpi", 46 | "Win32_UI_Shell", 47 | "Win32_UI_WindowsAndMessaging", 48 | ] } 49 | windows-numerics = "0.3" 50 | winui3 = { workspace = true, optional = true, features = [ 51 | "native", 52 | "UI_Xaml", 53 | "UI_Composition", 54 | ] } 55 | 56 | slim-detours-sys = { workspace = true, optional = true } 57 | sync-unsafe-cell = { workspace = true, optional = true } 58 | 59 | [build-dependencies] 60 | embed-resource = "3" 61 | 62 | [features] 63 | win32 = ["winio-handle/win32"] 64 | winui = ["winio-handle/winui", "dep:winui3"] 65 | dark-mode = ["dep:slim-detours-sys", "dep:sync-unsafe-cell"] 66 | 67 | once_cell_try = ["compio/once_cell_try"] 68 | nightly = ["once_cell_try"] 69 | -------------------------------------------------------------------------------- /winio-ui-windows-common/src/monitor.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::{addr_of_mut, null_mut}; 2 | 3 | use compio::driver::syscall; 4 | use compio_log::error; 5 | use windows_sys::{ 6 | Win32::{ 7 | Foundation::{LPARAM, RECT}, 8 | Graphics::Gdi::{EnumDisplayMonitors, GetMonitorInfoW, HDC, HMONITOR, MONITORINFO}, 9 | UI::{ 10 | HiDpi::{GetDpiForMonitor, MDT_EFFECTIVE_DPI}, 11 | WindowsAndMessaging::USER_DEFAULT_SCREEN_DPI, 12 | }, 13 | }, 14 | core::BOOL, 15 | }; 16 | use winio_primitive::{Monitor, Point, Rect, Size}; 17 | 18 | pub fn monitor_get_all() -> crate::Result> { 19 | let mut res = vec![]; 20 | syscall!( 21 | BOOL, 22 | EnumDisplayMonitors( 23 | null_mut(), 24 | null_mut(), 25 | Some(enum_monitor), 26 | addr_of_mut!(res) as _ 27 | ) 28 | )?; 29 | Ok(res) 30 | } 31 | 32 | unsafe extern "system" fn enum_monitor(m: HMONITOR, _: HDC, _: *mut RECT, res: LPARAM) -> BOOL { 33 | let res = unsafe { &mut *(res as *mut Vec) }; 34 | let mut info: MONITORINFO = unsafe { std::mem::zeroed() }; 35 | info.cbSize = size_of::() as _; 36 | if unsafe { GetMonitorInfoW(m, &mut info) } == 0 { 37 | error!("GetMonitorInfoW: {:?}", crate::Error::from_thread()); 38 | return 0; 39 | } 40 | let mut dpix = 0; 41 | let mut dpiy = 0; 42 | let r = unsafe { GetDpiForMonitor(m, MDT_EFFECTIVE_DPI, &mut dpix, &mut dpiy) }; 43 | if r < 0 { 44 | error!( 45 | "GetDpiForMonitor: {:?}", 46 | crate::Error::from_hresult(windows::core::HRESULT(r)) 47 | ); 48 | return 0; 49 | } 50 | res.push(Monitor::new( 51 | rect_from(info.rcMonitor), 52 | rect_from(info.rcWork), 53 | Size::new(dpix as f64, dpiy as f64) / USER_DEFAULT_SCREEN_DPI as f64, 54 | )); 55 | 1 56 | } 57 | 58 | #[inline] 59 | fn rect_from(r: RECT) -> Rect { 60 | Rect::new( 61 | Point::new(r.left as f64, r.top as f64), 62 | Size::new((r.right - r.left) as f64, (r.bottom - r.top) as f64), 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /winio-ui-winui/src/hook/mq.rs: -------------------------------------------------------------------------------- 1 | //! Hook GetMessage to wait for custom objects. 2 | 3 | use std::sync::LazyLock; 4 | 5 | use compio_log::error; 6 | use slim_detours_sys::SlimDetoursInlineHook; 7 | use sync_unsafe_cell::SyncUnsafeCell; 8 | use windows::{ 9 | Win32::{ 10 | Foundation::HANDLE, 11 | Storage::Packaging::Appx::{ 12 | AppPolicyGetWindowingModel, AppPolicyWindowingModel_ClassicDesktop, 13 | AppPolicyWindowingModel_None, 14 | }, 15 | }, 16 | core::Result, 17 | }; 18 | use windows_sys::Win32::{ 19 | Foundation::HWND, 20 | UI::WindowsAndMessaging::{GetMessageW, MSG}, 21 | }; 22 | 23 | type GetMessageWFn = 24 | unsafe extern "system" fn(msg: *mut MSG, hwnd: HWND, min: u32, max: u32) -> i32; 25 | 26 | static TRUE_GET_MESSAGE_W: SyncUnsafeCell = SyncUnsafeCell::new(GetMessageW); 27 | 28 | unsafe extern "system" fn get_message(msg: *mut MSG, hwnd: HWND, min: u32, max: u32) -> i32 { 29 | if let Some(res) = unsafe { crate::runtime::run_runtime(msg, hwnd, min, max) } { 30 | res 31 | } else { 32 | unsafe { (*TRUE_GET_MESSAGE_W.get())(msg, hwnd, min, max) } 33 | } 34 | } 35 | 36 | fn detour_attach() -> Result<()> { 37 | let res = 38 | unsafe { SlimDetoursInlineHook(1, TRUE_GET_MESSAGE_W.get().cast(), get_message as _) }; 39 | windows::core::HRESULT(res).ok() 40 | } 41 | 42 | static DETOUR_GUARD: LazyLock> = LazyLock::new(detour_attach); 43 | 44 | pub fn init_hook() -> bool { 45 | let desktop = is_desktop().unwrap_or_default(); 46 | desktop && { 47 | let res = &*DETOUR_GUARD; 48 | if let Err(_e) = res { 49 | error!("Failed to hook GetMessageW: {_e:?}"); 50 | } 51 | res.is_ok() 52 | } 53 | } 54 | 55 | fn is_desktop() -> Result { 56 | let mut policy = AppPolicyWindowingModel_None; 57 | unsafe { AppPolicyGetWindowingModel(GetCurrentThreadEffectiveToken(), &mut policy) }.ok()?; 58 | Ok(policy == AppPolicyWindowingModel_ClassicDesktop) 59 | } 60 | 61 | #[inline] 62 | #[allow(non_snake_case)] 63 | fn GetCurrentThreadEffectiveToken() -> HANDLE { 64 | HANDLE(-6_isize as _) 65 | } 66 | -------------------------------------------------------------------------------- /winio-ui-qt/src/runtime/qt.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, future::Future, os::fd::AsRawFd}; 2 | 3 | use cxx::UniquePtr; 4 | 5 | use crate::Result; 6 | 7 | #[cxx::bridge] 8 | mod ffi { 9 | unsafe extern "C++-unwind" { 10 | include!("winio-ui-qt/src/runtime/qt.hpp"); 11 | 12 | type WinioQtEventLoop; 13 | 14 | fn new_event_loop(args: Vec, fd: i32) -> Result>; 15 | 16 | fn process(self: Pin<&mut Self>); 17 | #[rust_name = "process_timeout"] 18 | fn process(self: Pin<&mut Self>, maxtime: i32); 19 | 20 | fn setAppName(self: Pin<&mut Self>, name: &str) -> Result<()>; 21 | } 22 | } 23 | 24 | pub struct Runtime { 25 | runtime: winio_pollable::Runtime, 26 | event_loop: RefCell>, 27 | } 28 | 29 | impl Runtime { 30 | pub fn new() -> Result { 31 | let runtime = winio_pollable::Runtime::new()?; 32 | let poll_fd = runtime.as_raw_fd(); 33 | let args = std::env::args().collect::>(); 34 | let event_loop = RefCell::new(ffi::new_event_loop(args, poll_fd)?); 35 | 36 | Ok(Self { 37 | runtime, 38 | event_loop, 39 | }) 40 | } 41 | 42 | pub fn set_app_id(&mut self, id: &str) -> Result<()> { 43 | self.event_loop.borrow_mut().pin_mut().setAppName(id)?; 44 | Ok(()) 45 | } 46 | 47 | pub(crate) fn run(&self) { 48 | self.runtime.run(); 49 | } 50 | 51 | fn enter T>(&self, f: F) -> T { 52 | self.runtime.enter(|| crate::RUNTIME.set(self, f)) 53 | } 54 | 55 | pub fn block_on(&self, future: F) -> F::Output { 56 | self.enter(|| { 57 | self.runtime.block_on(future, |timeout| { 58 | if let Some(timeout) = timeout { 59 | self.event_loop 60 | .borrow_mut() 61 | .pin_mut() 62 | .process_timeout(timeout.as_millis() as _); 63 | } else { 64 | self.event_loop.borrow_mut().pin_mut().process(); 65 | } 66 | }) 67 | }) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /winio/src/stub/ui/list_box.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct ListBox { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl ListBox { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn min_size(&self) -> Result; 29 | 30 | pub fn loc(&self) -> Result; 31 | 32 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 33 | 34 | pub fn size(&self) -> Result; 35 | 36 | pub fn set_size(&mut self, v: Size) -> Result<()>; 37 | 38 | pub fn tooltip(&self) -> Result; 39 | 40 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 41 | 42 | pub fn is_selected(&self, _i: usize) -> Result { 43 | not_impl() 44 | } 45 | 46 | pub fn set_selected(&mut self, _i: usize, _v: bool) -> Result<()> { 47 | not_impl() 48 | } 49 | 50 | pub fn len(&self) -> Result { 51 | not_impl() 52 | } 53 | 54 | pub fn is_empty(&self) -> Result { 55 | not_impl() 56 | } 57 | 58 | pub fn clear(&mut self) -> Result<()> { 59 | not_impl() 60 | } 61 | 62 | pub fn get(&self, _i: usize) -> Result { 63 | not_impl() 64 | } 65 | 66 | pub fn set(&mut self, _i: usize, _s: impl AsRef) -> Result<()> { 67 | not_impl() 68 | } 69 | 70 | pub fn insert(&mut self, _i: usize, _s: impl AsRef) -> Result<()> { 71 | not_impl() 72 | } 73 | 74 | pub fn remove(&mut self, _i: usize) -> Result<()> { 75 | not_impl() 76 | } 77 | 78 | pub async fn wait_select(&self) { 79 | not_impl() 80 | } 81 | } 82 | 83 | winio_handle::impl_as_widget!(ListBox, handle); 84 | -------------------------------------------------------------------------------- /winio/src/stub/ui/scroll_bar.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Orient, Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct ScrollBar { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl ScrollBar { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn preferred_size(&self) -> Result; 27 | 28 | pub fn loc(&self) -> Result; 29 | 30 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 31 | 32 | pub fn size(&self) -> Result; 33 | 34 | pub fn set_size(&mut self, v: Size) -> Result<()>; 35 | 36 | pub fn tooltip(&self) -> Result; 37 | 38 | pub fn set_tooltip(&mut self, s: impl AsRef) -> Result<()>; 39 | 40 | pub fn orient(&self) -> Result { 41 | not_impl() 42 | } 43 | 44 | pub fn set_orient(&mut self, _v: Orient) -> Result<()> { 45 | not_impl() 46 | } 47 | 48 | pub fn minimum(&self) -> Result { 49 | not_impl() 50 | } 51 | 52 | pub fn set_minimum(&mut self, _v: usize) -> Result<()> { 53 | not_impl() 54 | } 55 | 56 | pub fn maximum(&self) -> Result { 57 | not_impl() 58 | } 59 | 60 | pub fn set_maximum(&mut self, _v: usize) -> Result<()> { 61 | not_impl() 62 | } 63 | 64 | pub fn page(&self) -> Result { 65 | not_impl() 66 | } 67 | 68 | pub fn set_page(&mut self, _v: usize) -> Result<()> { 69 | not_impl() 70 | } 71 | 72 | pub fn pos(&self) -> Result { 73 | not_impl() 74 | } 75 | 76 | pub fn set_pos(&mut self, _pos: usize) -> Result<()> { 77 | not_impl() 78 | } 79 | 80 | pub async fn wait_change(&self) { 81 | not_impl() 82 | } 83 | } 84 | 85 | winio_handle::impl_as_widget!(ScrollBar, handle); 86 | -------------------------------------------------------------------------------- /winio/src/stub/ui/tab_view.rs: -------------------------------------------------------------------------------- 1 | use inherit_methods_macro::inherit_methods; 2 | use winio_handle::AsContainer; 3 | use winio_primitive::{Point, Size}; 4 | 5 | use crate::stub::{Result, Widget, not_impl}; 6 | 7 | #[derive(Debug)] 8 | pub struct TabView { 9 | handle: Widget, 10 | } 11 | 12 | #[inherit_methods(from = "self.handle")] 13 | impl TabView { 14 | pub fn new(_parent: impl AsContainer) -> Result { 15 | not_impl() 16 | } 17 | 18 | pub fn is_visible(&self) -> Result; 19 | 20 | pub fn set_visible(&mut self, v: bool) -> Result<()>; 21 | 22 | pub fn is_enabled(&self) -> Result; 23 | 24 | pub fn set_enabled(&mut self, v: bool) -> Result<()>; 25 | 26 | pub fn loc(&self) -> Result; 27 | 28 | pub fn set_loc(&mut self, p: Point) -> Result<()>; 29 | 30 | pub fn size(&self) -> Result; 31 | 32 | pub fn set_size(&mut self, v: Size) -> Result<()>; 33 | 34 | pub fn selection(&self) -> Result> { 35 | not_impl() 36 | } 37 | 38 | pub fn set_selection(&mut self, _i: usize) -> Result<()> { 39 | not_impl() 40 | } 41 | 42 | pub fn insert(&mut self, _i: usize, _item: &TabViewItem) -> Result<()> { 43 | not_impl() 44 | } 45 | 46 | pub fn remove(&mut self, _i: usize) -> Result<()> { 47 | not_impl() 48 | } 49 | 50 | pub fn len(&self) -> Result { 51 | not_impl() 52 | } 53 | 54 | pub fn is_empty(&self) -> Result { 55 | not_impl() 56 | } 57 | 58 | pub fn clear(&mut self) -> Result<()> { 59 | not_impl() 60 | } 61 | 62 | pub async fn wait_select(&self) { 63 | not_impl() 64 | } 65 | } 66 | 67 | winio_handle::impl_as_widget!(TabView, handle); 68 | 69 | #[derive(Debug)] 70 | pub struct TabViewItem { 71 | handle: Widget, 72 | } 73 | 74 | #[inherit_methods(from = "self.handle")] 75 | impl TabViewItem { 76 | pub fn new() -> Result { 77 | not_impl() 78 | } 79 | 80 | pub fn text(&self) -> Result; 81 | 82 | pub fn set_text(&mut self, s: impl AsRef) -> Result<()>; 83 | 84 | pub fn size(&self) -> Result; 85 | } 86 | 87 | winio_handle::impl_as_container!(TabViewItem, handle); 88 | -------------------------------------------------------------------------------- /winio-ui-win32/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winio-ui-win32" 3 | version = "0.4.0" 4 | description = "Win32 backend for winio." 5 | edition = { workspace = true } 6 | authors = { workspace = true } 7 | readme = { workspace = true } 8 | license = { workspace = true } 9 | repository = { workspace = true } 10 | 11 | [target.'cfg(windows)'.dependencies] 12 | winio-primitive = { workspace = true } 13 | winio-handle = { workspace = true, features = ["win32"] } 14 | winio-callback = { workspace = true, optional = true } 15 | winio-ui-windows-common = { workspace = true, features = ["win32"] } 16 | 17 | compio = { workspace = true, features = ["arrayvec"] } 18 | compio-log = { workspace = true } 19 | 20 | futures-util = { workspace = true } 21 | inherit-methods-macro = { workspace = true } 22 | local-sync = { workspace = true, optional = true } 23 | image = { workspace = true } 24 | once_cell = { workspace = true } 25 | percent-encoding = { workspace = true, optional = true } 26 | scoped-tls = { workspace = true } 27 | slab = "0.4" 28 | widestring = { workspace = true } 29 | 30 | windows-sys = { workspace = true, features = [ 31 | "Win32_Foundation", 32 | "Win32_Graphics_Gdi", 33 | "Win32_System_SystemServices", 34 | "Win32_System_Threading", 35 | "Win32_System_WindowsProgramming", 36 | "Win32_UI_Controls", 37 | "Win32_UI_HiDpi", 38 | "Win32_UI_Input_KeyboardAndMouse", 39 | "Win32_UI_WindowsAndMessaging", 40 | ] } 41 | windows = { workspace = true, features = [ 42 | "Win32_Foundation", 43 | "Win32_Graphics_Direct2D", 44 | "Win32_Graphics_Direct2D_Common", 45 | "Win32_Graphics_DirectWrite", 46 | "Win32_Graphics_Dxgi_Common", 47 | ] } 48 | windows-core = { workspace = true, optional = true } 49 | webview2 = { workspace = true, optional = true } 50 | 51 | [features] 52 | media = [ 53 | "dep:winio-callback", 54 | "dep:windows-core", 55 | "dep:percent-encoding", 56 | "windows/Win32_Media_MediaFoundation", 57 | ] 58 | webview = [ 59 | "dep:winio-callback", 60 | "dep:webview2", 61 | "dep:windows-core", 62 | "dep:local-sync", 63 | ] 64 | 65 | dark-mode = ["winio-ui-windows-common/dark-mode"] 66 | 67 | once_cell_try = ["winio-ui-windows-common/once_cell_try"] 68 | nightly = ["once_cell_try"] 69 | -------------------------------------------------------------------------------- /winio/examples/subviews/dummy.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use winio::prelude::*; 4 | 5 | use crate::{Error, Result}; 6 | 7 | pub struct DummyPage { 8 | window: Child, 9 | label: Child