wip: migrate to gtk4
This commit is contained in:
658
Cargo.lock
generated
658
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@ derive_more = { version = "1", features = [
|
||||
extend = "1.2"
|
||||
futures = "0.3.30"
|
||||
grass = "0.13.4"
|
||||
gtk = "0.18.1"
|
||||
gtk4 = "0.10.1"
|
||||
itertools = "0.13.0"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
|
||||
@@ -12,16 +12,15 @@ edition = "2021"
|
||||
[features]
|
||||
default = ["x11", "wayland"]
|
||||
x11 = ["gdkx11", "x11rb"]
|
||||
wayland = ["gtk-layer-shell"]
|
||||
wayland = ["gtk4-layer-shell"]
|
||||
|
||||
[dependencies]
|
||||
shared_utils.workspace = true
|
||||
rhai_impl.workspace = true
|
||||
|
||||
gtk-layer-shell = { version = "0.8.1", optional = true, features=["v0_6"] }
|
||||
gtk4-layer-shell = { version = "0.6.3", optional = true }
|
||||
gdkx11 = { version = "0.18", optional = true }
|
||||
x11rb = { version = "0.13.1", features = ["randr"], optional = true }
|
||||
gdk-sys = "0.18.0"
|
||||
|
||||
grass.workspace = true
|
||||
thiserror.workspace = true
|
||||
@@ -35,7 +34,7 @@ derive_more.workspace = true
|
||||
extend.workspace = true
|
||||
futures.workspace = true
|
||||
smart-default.workspace = true
|
||||
gtk.workspace = true
|
||||
gtk4.workspace = true
|
||||
itertools.workspace = true
|
||||
log.workspace = true
|
||||
nix = { workspace = true, features = ["process", "fs", "signal"] }
|
||||
|
||||
@@ -3,8 +3,8 @@ use crate::{
|
||||
daemon_response::DaemonResponseSender,
|
||||
display_backend::DisplayBackend,
|
||||
error_handling_ctx,
|
||||
gtk::prelude::{
|
||||
ContainerExt, CssProviderExt, GtkWindowExt, MonitorExt, StyleContextExt, WidgetExt,
|
||||
gtk4::prelude::{
|
||||
ContainerExt, CssProviderExt, GtkWindowExt, MonitorExt, StyleContextExt, WidgetExt, ObjectExt, ApplicationExt, CellAreaExt, GskRendererExt
|
||||
},
|
||||
paths::EwwiiPaths,
|
||||
widgets::window::Window,
|
||||
@@ -23,10 +23,9 @@ use crate::{
|
||||
*,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
use codespan_reporting::files::Files;
|
||||
use gdk::Monitor;
|
||||
use glib::ObjectExt;
|
||||
use gtk::{gdk, glib};
|
||||
use gtk4::{gdk, glib};
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use rhai::Dynamic;
|
||||
@@ -143,7 +142,7 @@ pub struct App<B: DisplayBackend> {
|
||||
/// Window names that are supposed to be open, but failed.
|
||||
/// When reloading the config, these should be opened again.
|
||||
pub failed_windows: HashSet<String>,
|
||||
pub css_provider: gtk::CssProvider,
|
||||
pub css_provider: gtk4::CssProvider,
|
||||
|
||||
/// Sender to send [`DaemonCommand`]s
|
||||
pub app_evt_send: UnboundedSender<DaemonCommand>,
|
||||
@@ -160,6 +159,7 @@ pub struct App<B: DisplayBackend> {
|
||||
pub rt_engine_config: EngineConfValues,
|
||||
|
||||
pub paths: EwwiiPaths,
|
||||
pub gtk_app: gtk4::Application,
|
||||
pub phantom: PhantomData<B>,
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ impl<B: DisplayBackend> App<B> {
|
||||
match event {
|
||||
DaemonCommand::NoOp => {}
|
||||
DaemonCommand::OpenInspector => {
|
||||
gtk::Window::set_interactive_debugging(true);
|
||||
gtk4::Window::set_interactive_debugging(true);
|
||||
}
|
||||
DaemonCommand::ReloadConfigAndCss(sender) => {
|
||||
// Wait for all monitor models to be set. When a new monitor gets added, this
|
||||
@@ -364,7 +364,7 @@ impl<B: DisplayBackend> App<B> {
|
||||
for (_, window) in self.open_windows.drain() {
|
||||
window.close();
|
||||
}
|
||||
gtk::main_quit();
|
||||
self.gtk_app.quit();
|
||||
let _ = crate::application_lifecycle::send_exit();
|
||||
}
|
||||
|
||||
@@ -599,10 +599,10 @@ impl<B: DisplayBackend> App<B> {
|
||||
|
||||
// handling users close request
|
||||
ewwii_window.delete_event_handler_id =
|
||||
Some(ewwii_window.gtk_window.connect_delete_event({
|
||||
Some(ewwii_window.gtk_window.connect_close_request({
|
||||
let handler = gtk_close_handler.clone();
|
||||
let closed_by_user = closed_by_user.clone();
|
||||
move |_, _| {
|
||||
move |_| {
|
||||
handler(false); // -- false: don't reopen window to respect users intent
|
||||
closed_by_user.set(true);
|
||||
glib::Propagation::Proceed
|
||||
@@ -688,26 +688,10 @@ impl<B: DisplayBackend> App<B> {
|
||||
}
|
||||
|
||||
/// Load a given CSS string into the gtk css provider, returning a nicely formatted [`DiagError`] when GTK errors out
|
||||
pub fn load_css(&mut self, file_id: usize, css: &str) -> Result<()> {
|
||||
if let Err(err) = self.css_provider.load_from_data(css.as_bytes()) {
|
||||
static PATTERN: Lazy<regex::Regex> =
|
||||
Lazy::new(|| regex::Regex::new(r"[^:]*:(\d+):(\d+)(.*)$").unwrap());
|
||||
let nice_error_option: Option<_> = (|| {
|
||||
let captures = PATTERN.captures(err.message())?;
|
||||
let line = captures.get(1).unwrap().as_str().parse::<usize>().ok()?;
|
||||
let msg = captures.get(3).unwrap().as_str();
|
||||
let db = error_handling_ctx::FILE_DATABASE.read().ok()?;
|
||||
let line_range = db.line_range(file_id, line - 1).ok()?;
|
||||
let span = Span(line_range.start, line_range.end - 1, file_id);
|
||||
Some(DiagError(gen_diagnostic!(msg, span)))
|
||||
})();
|
||||
match nice_error_option {
|
||||
Some(error) => Err(anyhow!(error)),
|
||||
None => Err(anyhow!("CSS error: {}", err.message())),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
pub fn load_css(&mut self, _file_id: usize, css: &str) -> Result<()> {
|
||||
self.css_provider.load_from_data(&css);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Trigger a UI update with the given flags.
|
||||
@@ -847,7 +831,7 @@ where
|
||||
fn initialize_window<B: DisplayBackend>(
|
||||
window_init: &WindowInitiator,
|
||||
monitor: Monitor,
|
||||
root_widget: gtk::Widget,
|
||||
root_widget: gtk4::Widget,
|
||||
) -> Result<EwwiiWindow> {
|
||||
let monitor_geometry = monitor.geometry();
|
||||
let (actual_window_rect, x, y) = match window_init.geometry {
|
||||
@@ -861,8 +845,8 @@ fn initialize_window<B: DisplayBackend>(
|
||||
format!("monitor {} is unavailable", window_init.monitor.clone().unwrap())
|
||||
})?;
|
||||
|
||||
window.set_title(&format!("Ewwii - {}", window_init.name));
|
||||
window.set_position(gtk::WindowPosition::None);
|
||||
window.set_title(Some(&format!("Ewwii - {}", window_init.name)));
|
||||
// window.set_position(gtk4::WindowPosition::None);
|
||||
window.set_gravity(gdk::Gravity::Center);
|
||||
|
||||
if let Some(actual_window_rect) = actual_window_rect {
|
||||
@@ -968,21 +952,24 @@ fn apply_window_position(
|
||||
monitor_geometry: gdk::Rectangle,
|
||||
window: &Window,
|
||||
) -> Result<()> {
|
||||
let gdk_window = window.window().context("Failed to get gdk window from gtk window")?;
|
||||
window_geometry.size = crate::window::window_geometry::Coords::from_pixels(window.size());
|
||||
let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry);
|
||||
let gdk_window = window.surface().context("Failed to get gdk surface from gtk window")?;
|
||||
|
||||
if let Some(x11_surface) = surface.downcast_ref::<gdkx11::X11Surface>() {
|
||||
window_geometry.size = crate::window::window_geometry::Coords::from_pixels(window.default_size());
|
||||
let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry);
|
||||
|
||||
let gdk_origin = gdk_window.origin();
|
||||
let (origin_x, origin_y) = x11_surface.position();
|
||||
|
||||
if actual_window_rect.x() != gdk_origin.1 || actual_window_rect.y() != gdk_origin.2 {
|
||||
gdk_window.move_(actual_window_rect.x(), actual_window_rect.y());
|
||||
if actual_window_rect.x() != origin_x || actual_window_rect.y() != origin_y {
|
||||
x11_surface.move_(actual_window_rect.x(), actual_window_rect.y());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_screen_changed(window: &Window, _old_screen: Option<&gdk::Screen>) {
|
||||
let visual = gtk::prelude::GtkWindowExt::screen(window).and_then(|screen| {
|
||||
let visual = gtk4::prelude::GtkWindowExt::screen(window).and_then(|screen| {
|
||||
screen.rgba_visual().filter(|_| screen.is_composited()).or_else(|| screen.system_visual())
|
||||
});
|
||||
window.set_visual(visual.as_ref());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
|
||||
|
||||
use gtk::gdk;
|
||||
use gtk4::gdk;
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub use platform_wayland::WaylandBackend;
|
||||
@@ -35,7 +35,11 @@ impl DisplayBackend for NoBackend {
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Option<Window> {
|
||||
Some(Window::new(gtk::WindowType::Toplevel, x, y))
|
||||
// top level
|
||||
let window = Window::new();
|
||||
window.move_(x, y);
|
||||
|
||||
Some(window)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +50,9 @@ mod platform_wayland {
|
||||
use crate::window::window_definition::WindowStacking;
|
||||
use crate::window::window_geometry::AnchorAlignment;
|
||||
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
|
||||
use gtk::gdk;
|
||||
use gtk::prelude::*;
|
||||
use gtk_layer_shell::{KeyboardMode, LayerShell};
|
||||
use gtk4::gdk;
|
||||
use gtk4::prelude::*;
|
||||
use gtk4_layer_shell::{KeyboardMode, LayerShell};
|
||||
|
||||
pub struct WaylandBackend;
|
||||
|
||||
@@ -62,7 +66,9 @@ mod platform_wayland {
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Option<Window> {
|
||||
let window = Window::new(gtk::WindowType::Toplevel, x, y);
|
||||
// top level
|
||||
let window = Window::new();
|
||||
window.move_(x, y);
|
||||
|
||||
// Sets the keyboard interactivity
|
||||
match window_init.backend_options.wayland.focusable {
|
||||
@@ -88,12 +94,12 @@ mod platform_wayland {
|
||||
|
||||
// Sets the layer where the layer shell surface will spawn
|
||||
match window_init.stacking {
|
||||
WindowStacking::Foreground => window.set_layer(gtk_layer_shell::Layer::Top),
|
||||
WindowStacking::Foreground => window.set_layer(gtk4_layer_shell::Layer::Top),
|
||||
WindowStacking::Background => {
|
||||
window.set_layer(gtk_layer_shell::Layer::Background)
|
||||
window.set_layer(gtk4_layer_shell::Layer::Background)
|
||||
}
|
||||
WindowStacking::Bottom => window.set_layer(gtk_layer_shell::Layer::Bottom),
|
||||
WindowStacking::Overlay => window.set_layer(gtk_layer_shell::Layer::Overlay),
|
||||
WindowStacking::Bottom => window.set_layer(gtk4_layer_shell::Layer::Bottom),
|
||||
WindowStacking::Overlay => window.set_layer(gtk4_layer_shell::Layer::Overlay),
|
||||
}
|
||||
|
||||
if let Some(namespace) = &window_init.backend_options.wayland.namespace {
|
||||
@@ -118,23 +124,23 @@ mod platform_wayland {
|
||||
AnchorAlignment::END => bottom = true,
|
||||
}
|
||||
|
||||
window.set_anchor(gtk_layer_shell::Edge::Left, left);
|
||||
window.set_anchor(gtk_layer_shell::Edge::Right, right);
|
||||
window.set_anchor(gtk_layer_shell::Edge::Top, top);
|
||||
window.set_anchor(gtk_layer_shell::Edge::Bottom, bottom);
|
||||
window.set_anchor(gtk4_layer_shell::Edge::Left, left);
|
||||
window.set_anchor(gtk4_layer_shell::Edge::Right, right);
|
||||
window.set_anchor(gtk4_layer_shell::Edge::Top, top);
|
||||
window.set_anchor(gtk4_layer_shell::Edge::Bottom, bottom);
|
||||
|
||||
let xoffset = geometry.offset.x.pixels_relative_to(monitor.width());
|
||||
let yoffset = geometry.offset.y.pixels_relative_to(monitor.height());
|
||||
|
||||
if left {
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Left, xoffset);
|
||||
window.set_layer_shell_margin(gtk4_layer_shell::Edge::Left, xoffset);
|
||||
} else {
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Right, xoffset);
|
||||
window.set_layer_shell_margin(gtk4_layer_shell::Edge::Right, xoffset);
|
||||
}
|
||||
if bottom {
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Bottom, yoffset);
|
||||
window.set_layer_shell_margin(gtk4_layer_shell::Edge::Bottom, yoffset);
|
||||
} else {
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, yoffset);
|
||||
window.set_layer_shell_margin(gtk4_layer_shell::Edge::Top, yoffset);
|
||||
}
|
||||
// https://github.com/elkowar/eww/issues/296
|
||||
if window_init.backend_options.wayland.exclusive
|
||||
@@ -162,8 +168,8 @@ mod platform_x11 {
|
||||
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
|
||||
use anyhow::{Context, Result};
|
||||
use gdk::Monitor;
|
||||
use gtk::gdk;
|
||||
use gtk::{self, prelude::*};
|
||||
use gtk4::gdk;
|
||||
use gtk4::{self, prelude::*};
|
||||
use x11rb::protocol::xproto::ConnectionExt;
|
||||
|
||||
use x11rb::{
|
||||
@@ -186,12 +192,13 @@ mod platform_x11 {
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Option<Window> {
|
||||
let window = Window::new();
|
||||
window.move_(x, y);
|
||||
|
||||
let window_type = if window_init.backend_options.x11.wm_ignore {
|
||||
gtk::WindowType::Popup
|
||||
} else {
|
||||
gtk::WindowType::Toplevel
|
||||
};
|
||||
let window = Window::new(window_type, x, y);
|
||||
window.modal(true);
|
||||
}; // else: normal, which is toplevel
|
||||
|
||||
window.set_resizable(window_init.resizable);
|
||||
window.set_keep_above(window_init.stacking == WindowStacking::Foreground);
|
||||
window.set_keep_below(window_init.stacking == WindowStacking::Background);
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
#![allow(rustdoc::private_intra_doc_links)]
|
||||
|
||||
// getting gtk stuff
|
||||
extern crate gtk;
|
||||
extern crate gtk4;
|
||||
#[cfg(feature = "wayland")]
|
||||
extern crate gtk_layer_shell as gtk_layer_shell;
|
||||
extern crate gtk4_layer_shell as gtk4_layer_shell;
|
||||
|
||||
// imporing dependencies
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
@@ -76,74 +76,80 @@ pub fn initialize_server<B: DisplayBackend>(
|
||||
if B::IS_WAYLAND {
|
||||
std::env::set_var("GDK_BACKEND", "wayland")
|
||||
}
|
||||
gtk::init()?;
|
||||
|
||||
let mut app: App<B> = app::App {
|
||||
ewwii_config,
|
||||
open_windows: HashMap::new(),
|
||||
failed_windows: HashSet::new(),
|
||||
instance_id_to_args: HashMap::new(),
|
||||
css_provider: gtk::CssProvider::new(),
|
||||
app_evt_send: ui_send.clone(),
|
||||
window_close_timer_abort_senders: HashMap::new(),
|
||||
widget_reg_store: std::rc::Rc::new(std::sync::Mutex::new(None)),
|
||||
pl_handler_store: None,
|
||||
rt_engine_config: EngineConfValues::default(),
|
||||
paths,
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
if let Some(screen) = gtk::gdk::Screen::default() {
|
||||
gtk::StyleContext::add_provider_for_screen(
|
||||
&screen,
|
||||
&app.css_provider,
|
||||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
}
|
||||
|
||||
if let Ok((file_id, css)) = config::scss::parse_scss_from_config(app.paths.get_config_dir()) {
|
||||
if let Err(e) = app.load_css(file_id, &css) {
|
||||
error_handling_ctx::print_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
let gtk_app = gtk4::Application::builder()
|
||||
.application_id("com.widgetsystem.ewwii")
|
||||
.build();
|
||||
|
||||
connect_monitor_added(ui_send.clone());
|
||||
|
||||
// initialize all the handlers and tasks running asyncronously
|
||||
let tokio_handle = init_async_part(app.paths.clone(), ui_send);
|
||||
|
||||
gtk::glib::MainContext::default().spawn_local(async move {
|
||||
// if an action was given to the daemon initially, execute it first.
|
||||
if let Some(action) = action {
|
||||
app.handle_command(action).await;
|
||||
}
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// Some(scope_graph_evt) = scope_graph_evt_recv.recv() => {
|
||||
// app.scope_graph.borrow_mut().handle_scope_graph_event(scope_graph_evt);
|
||||
// },
|
||||
Some(ui_event) = ui_recv.recv() => {
|
||||
app.handle_command(ui_event).await;
|
||||
}
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
});
|
||||
let tokio_handle = init_async_part(paths.clone(), ui_send);
|
||||
|
||||
// allow the GTK main thread to do tokio things
|
||||
let _g = tokio_handle.enter();
|
||||
|
||||
gtk::main();
|
||||
log::info!("main application thread finished");
|
||||
gtk_app.connect_activate(move |gtk_app| {
|
||||
let mut app: App<B> = app::App {
|
||||
ewwii_config: ewwii_config.clone(),
|
||||
open_windows: HashMap::new(),
|
||||
failed_windows: HashSet::new(),
|
||||
instance_id_to_args: HashMap::new(),
|
||||
css_provider: gtk4::CssProvider::new(),
|
||||
app_evt_send: ui_send.clone(),
|
||||
window_close_timer_abort_senders: HashMap::new(),
|
||||
widget_reg_store: std::rc::Rc::new(std::sync::Mutex::new(None)),
|
||||
pl_handler_store: None,
|
||||
rt_engine_config: EngineConfValues::default(),
|
||||
paths: paths.clone(),
|
||||
gtk_app: gtk_app.clone(),
|
||||
phantom: PhantomData,
|
||||
};
|
||||
|
||||
if let Some(screen) = gtk4::gdk::Screen::default() {
|
||||
gtk4::StyleContext::add_provider_for_screen(
|
||||
&screen,
|
||||
&app.css_provider,
|
||||
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
}
|
||||
|
||||
if let Ok((file_id, css)) = config::scss::parse_scss_from_config(app.paths.get_config_dir()) {
|
||||
if let Err(e) = app.load_css(file_id, &css) {
|
||||
error_handling_ctx::print_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
gtk4::glib::MainContext::default().spawn_local(async move {
|
||||
// if an action was given to the daemon initially, execute it first.
|
||||
if let Some(action) = action {
|
||||
app.handle_command(action).await;
|
||||
}
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// Some(scope_graph_evt) = scope_graph_evt_recv.recv() => {
|
||||
// app.scope_graph.borrow_mut().handle_scope_graph_event(scope_graph_evt);
|
||||
// },
|
||||
Some(ui_event) = ui_recv.recv() => {
|
||||
app.handle_command(ui_event).await;
|
||||
}
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let exit_status = gtk_app.run();
|
||||
log::info!("main application thread finished with exit status: {}", exit_status);
|
||||
|
||||
Ok(ForkResult::Child)
|
||||
}
|
||||
|
||||
fn connect_monitor_added(ui_send: UnboundedSender<DaemonCommand>) {
|
||||
let display = gtk::gdk::Display::default().expect("could not get default display");
|
||||
let display = gtk4::gdk::Display::default().expect("could not get default display");
|
||||
display.connect_monitor_added({
|
||||
move |_display: >k::gdk::Display, _monitor: >k::gdk::Monitor| {
|
||||
move |_display: >k4::gdk::Display, _monitor: >k4::gdk::Monitor| {
|
||||
log::info!("New monitor connected, reloading configuration");
|
||||
let _ = reload_config_and_css(&ui_send);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use gtk::gdk::prelude::Cast;
|
||||
use gtk4::gdk::prelude::Cast;
|
||||
|
||||
use crate::{config::WindowDefinition, widgets::widget_definitions::*};
|
||||
|
||||
@@ -17,7 +17,7 @@ pub enum WidgetInput<'a> {
|
||||
pub fn build_gtk_widget<'a>(
|
||||
input: &'a WidgetInput<'a>,
|
||||
widget_reg: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Widget> {
|
||||
) -> Result<gtk4::Widget> {
|
||||
let node: &'a WidgetNode = match input {
|
||||
WidgetInput::Node(n) => n,
|
||||
WidgetInput::BorrowedNode(n) => n,
|
||||
@@ -30,7 +30,7 @@ pub fn build_gtk_widget<'a>(
|
||||
fn build_gtk_widget_from_node(
|
||||
root_node: &WidgetNode,
|
||||
widget_reg: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Widget> {
|
||||
) -> Result<gtk4::Widget> {
|
||||
/*
|
||||
When a a new widget is added to the build process,
|
||||
make sure to update get_id_to_props_map() found in
|
||||
@@ -44,7 +44,7 @@ fn build_gtk_widget_from_node(
|
||||
build_center_box(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::EventBox { props, children } => {
|
||||
build_gtk_event_box(props, children, widget_reg)?.upcast()
|
||||
build_event_box(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::ToolTip { props, children } => {
|
||||
build_tooltip(props, children, widget_reg)?.upcast()
|
||||
|
||||
@@ -1,273 +1,273 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use gtk::glib::{self, object_subclass, prelude::*, wrapper, Properties};
|
||||
use gtk::{cairo, gdk, prelude::*, subclass::prelude::*};
|
||||
use std::cell::RefCell;
|
||||
// use anyhow::{anyhow, Result};
|
||||
// use gtk4::glib::{self, object_subclass, prelude::*, wrapper, Properties};
|
||||
// use gtk4::{cairo, gdk, prelude::*, subclass::prelude::*};
|
||||
// use std::cell::RefCell;
|
||||
|
||||
use crate::error_handling_ctx;
|
||||
// use crate::error_handling_ctx;
|
||||
|
||||
wrapper! {
|
||||
pub struct CircProg(ObjectSubclass<CircProgPriv>)
|
||||
@extends gtk::Bin, gtk::Container, gtk::Widget;
|
||||
}
|
||||
// wrapper! {
|
||||
// pub struct CircProg(ObjectSubclass<CircProgPriv>)
|
||||
// @extends gtk4::Bin, gtk4::Container, gtk4::Widget;
|
||||
// }
|
||||
|
||||
#[derive(Properties)]
|
||||
#[properties(wrapper_type = CircProg)]
|
||||
pub struct CircProgPriv {
|
||||
#[property(
|
||||
get,
|
||||
set,
|
||||
nick = "Starting at",
|
||||
blurb = "Starting at",
|
||||
minimum = 0f64,
|
||||
maximum = 100f64,
|
||||
default = 0f64
|
||||
)]
|
||||
start_at: RefCell<f64>,
|
||||
// #[derive(Properties)]
|
||||
// #[properties(wrapper_type = CircProg)]
|
||||
// pub struct CircProgPriv {
|
||||
// #[property(
|
||||
// get,
|
||||
// set,
|
||||
// nick = "Starting at",
|
||||
// blurb = "Starting at",
|
||||
// minimum = 0f64,
|
||||
// maximum = 100f64,
|
||||
// default = 0f64
|
||||
// )]
|
||||
// start_at: RefCell<f64>,
|
||||
|
||||
#[property(
|
||||
get,
|
||||
set,
|
||||
nick = "Value",
|
||||
blurb = "The value",
|
||||
minimum = 0f64,
|
||||
maximum = 100f64,
|
||||
default = 0f64
|
||||
)]
|
||||
value: RefCell<f64>,
|
||||
// #[property(
|
||||
// get,
|
||||
// set,
|
||||
// nick = "Value",
|
||||
// blurb = "The value",
|
||||
// minimum = 0f64,
|
||||
// maximum = 100f64,
|
||||
// default = 0f64
|
||||
// )]
|
||||
// value: RefCell<f64>,
|
||||
|
||||
#[property(
|
||||
get,
|
||||
set,
|
||||
nick = "Thickness",
|
||||
blurb = "Thickness",
|
||||
minimum = 0f64,
|
||||
maximum = 100f64,
|
||||
default = 1f64
|
||||
)]
|
||||
thickness: RefCell<f64>,
|
||||
// #[property(
|
||||
// get,
|
||||
// set,
|
||||
// nick = "Thickness",
|
||||
// blurb = "Thickness",
|
||||
// minimum = 0f64,
|
||||
// maximum = 100f64,
|
||||
// default = 1f64
|
||||
// )]
|
||||
// thickness: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Clockwise", blurb = "Clockwise", default = true)]
|
||||
clockwise: RefCell<bool>,
|
||||
// #[property(get, set, nick = "Clockwise", blurb = "Clockwise", default = true)]
|
||||
// clockwise: RefCell<bool>,
|
||||
|
||||
content: RefCell<Option<gtk::Widget>>,
|
||||
}
|
||||
// content: RefCell<Option<gtk4::Widget>>,
|
||||
// }
|
||||
|
||||
// This should match the default values from the ParamSpecs
|
||||
impl Default for CircProgPriv {
|
||||
fn default() -> Self {
|
||||
CircProgPriv {
|
||||
start_at: RefCell::new(0.0),
|
||||
value: RefCell::new(0.0),
|
||||
thickness: RefCell::new(1.0),
|
||||
clockwise: RefCell::new(true),
|
||||
content: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
// // This should match the default values from the ParamSpecs
|
||||
// impl Default for CircProgPriv {
|
||||
// fn default() -> Self {
|
||||
// CircProgPriv {
|
||||
// start_at: RefCell::new(0.0),
|
||||
// value: RefCell::new(0.0),
|
||||
// thickness: RefCell::new(1.0),
|
||||
// clockwise: RefCell::new(true),
|
||||
// content: RefCell::new(None),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ObjectImpl for CircProgPriv {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
Self::derived_properties()
|
||||
}
|
||||
// impl ObjectImpl for CircProgPriv {
|
||||
// fn properties() -> &'static [glib::ParamSpec] {
|
||||
// Self::derived_properties()
|
||||
// }
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"value" => {
|
||||
self.value.replace(value.get().unwrap());
|
||||
self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
}
|
||||
"thickness" => {
|
||||
self.thickness.replace(value.get().unwrap());
|
||||
}
|
||||
"start-at" => {
|
||||
self.start_at.replace(value.get().unwrap());
|
||||
}
|
||||
"clockwise" => {
|
||||
self.clockwise.replace(value.get().unwrap());
|
||||
}
|
||||
x => panic!("Tried to set inexistant property of CircProg: {}", x,),
|
||||
}
|
||||
}
|
||||
// fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
// match pspec.name() {
|
||||
// "value" => {
|
||||
// self.value.replace(value.get().unwrap());
|
||||
// self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
// }
|
||||
// "thickness" => {
|
||||
// self.thickness.replace(value.get().unwrap());
|
||||
// }
|
||||
// "start-at" => {
|
||||
// self.start_at.replace(value.get().unwrap());
|
||||
// }
|
||||
// "clockwise" => {
|
||||
// self.clockwise.replace(value.get().unwrap());
|
||||
// }
|
||||
// x => panic!("Tried to set inexistant property of CircProg: {}", x,),
|
||||
// }
|
||||
// }
|
||||
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.derived_property(id, pspec)
|
||||
}
|
||||
}
|
||||
// fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
// self.derived_property(id, pspec)
|
||||
// }
|
||||
// }
|
||||
|
||||
#[object_subclass]
|
||||
impl ObjectSubclass for CircProgPriv {
|
||||
type ParentType = gtk::Bin;
|
||||
type Type = CircProg;
|
||||
// #[object_subclass]
|
||||
// impl ObjectSubclass for CircProgPriv {
|
||||
// type ParentType = gtk4::Bin;
|
||||
// type Type = CircProg;
|
||||
|
||||
const NAME: &'static str = "CircProg";
|
||||
// const NAME: &'static str = "CircProg";
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.set_css_name("circular-progress");
|
||||
}
|
||||
}
|
||||
// fn class_init(klass: &mut Self::Class) {
|
||||
// klass.set_css_name("circular-progress");
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Default for CircProg {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
// impl Default for CircProg {
|
||||
// fn default() -> Self {
|
||||
// Self::new()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl CircProg {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new::<Self>()
|
||||
}
|
||||
}
|
||||
// impl CircProg {
|
||||
// pub fn new() -> Self {
|
||||
// glib::Object::new::<Self>()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ContainerImpl for CircProgPriv {
|
||||
fn add(&self, widget: >k::Widget) {
|
||||
if let Some(content) = &*self.content.borrow() {
|
||||
// TODO: Handle this error when populating children widgets instead
|
||||
error_handling_ctx::print_error(anyhow!(
|
||||
"Error, trying to add multiple children to a circular-progress widget"
|
||||
));
|
||||
self.parent_remove(content);
|
||||
}
|
||||
self.parent_add(widget);
|
||||
self.content.replace(Some(widget.clone()));
|
||||
}
|
||||
}
|
||||
// impl ContainerImpl for CircProgPriv {
|
||||
// fn add(&self, widget: >k4::Widget) {
|
||||
// if let Some(content) = &*self.content.borrow() {
|
||||
// // TODO: Handle this error when populating children widgets instead
|
||||
// error_handling_ctx::print_error(anyhow!(
|
||||
// "Error, trying to add multiple children to a circular-progress widget"
|
||||
// ));
|
||||
// self.parent_remove(content);
|
||||
// }
|
||||
// self.parent_add(widget);
|
||||
// self.content.replace(Some(widget.clone()));
|
||||
// }
|
||||
// }
|
||||
|
||||
fn calc_widget_lowest_preferred_dimension(widget: >k::Widget) -> (i32, i32) {
|
||||
let preferred_width = widget.preferred_width();
|
||||
let preferred_height = widget.preferred_height();
|
||||
let min_lowest = i32::min(preferred_width.0, preferred_height.0);
|
||||
let natural_lowest = i32::min(preferred_width.1, preferred_height.1);
|
||||
(min_lowest, natural_lowest)
|
||||
}
|
||||
// fn calc_widget_lowest_preferred_dimension(widget: >k4::Widget) -> (i32, i32) {
|
||||
// let preferred_width = widget.preferred_width();
|
||||
// let preferred_height = widget.preferred_height();
|
||||
// let min_lowest = i32::min(preferred_width.0, preferred_height.0);
|
||||
// let natural_lowest = i32::min(preferred_width.1, preferred_height.1);
|
||||
// (min_lowest, natural_lowest)
|
||||
// }
|
||||
|
||||
impl BinImpl for CircProgPriv {}
|
||||
// impl BinImpl for CircProgPriv {}
|
||||
|
||||
impl WidgetImpl for CircProgPriv {
|
||||
// We overwrite preferred_* so that overflowing content from the children gets cropped
|
||||
// We return min(child_width, child_height)
|
||||
fn preferred_width(&self) -> (i32, i32) {
|
||||
let styles = self.obj().style_context();
|
||||
let margin = styles.margin(gtk::StateFlags::NORMAL);
|
||||
// impl WidgetImpl for CircProgPriv {
|
||||
// // We overwrite preferred_* so that overflowing content from the children gets cropped
|
||||
// // We return min(child_width, child_height)
|
||||
// fn preferred_width(&self) -> (i32, i32) {
|
||||
// let styles = self.obj().style_context();
|
||||
// let margin = styles.margin(gtk4::StateFlags::NORMAL);
|
||||
|
||||
if let Some(child) = &*self.content.borrow() {
|
||||
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
|
||||
(
|
||||
min_child + margin.right as i32 + margin.left as i32,
|
||||
natural_child + margin.right as i32 + margin.left as i32,
|
||||
)
|
||||
} else {
|
||||
let empty_width =
|
||||
(2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
|
||||
(empty_width, empty_width)
|
||||
}
|
||||
}
|
||||
// if let Some(child) = &*self.content.borrow() {
|
||||
// let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
|
||||
// (
|
||||
// min_child + margin.right as i32 + margin.left as i32,
|
||||
// natural_child + margin.right as i32 + margin.left as i32,
|
||||
// )
|
||||
// } else {
|
||||
// let empty_width =
|
||||
// (2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
|
||||
// (empty_width, empty_width)
|
||||
// }
|
||||
// }
|
||||
|
||||
fn preferred_width_for_height(&self, _height: i32) -> (i32, i32) {
|
||||
self.preferred_width()
|
||||
}
|
||||
// fn preferred_width_for_height(&self, _height: i32) -> (i32, i32) {
|
||||
// self.preferred_width()
|
||||
// }
|
||||
|
||||
fn preferred_height(&self) -> (i32, i32) {
|
||||
let styles = self.obj().style_context();
|
||||
let margin = styles.margin(gtk::StateFlags::NORMAL);
|
||||
// fn preferred_height(&self) -> (i32, i32) {
|
||||
// let styles = self.obj().style_context();
|
||||
// let margin = styles.margin(gtk4::StateFlags::NORMAL);
|
||||
|
||||
if let Some(child) = &*self.content.borrow() {
|
||||
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
|
||||
(
|
||||
min_child + margin.bottom as i32 + margin.top as i32,
|
||||
natural_child + margin.bottom as i32 + margin.top as i32,
|
||||
)
|
||||
} else {
|
||||
let empty_height =
|
||||
(2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
|
||||
(empty_height, empty_height)
|
||||
}
|
||||
}
|
||||
// if let Some(child) = &*self.content.borrow() {
|
||||
// let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
|
||||
// (
|
||||
// min_child + margin.bottom as i32 + margin.top as i32,
|
||||
// natural_child + margin.bottom as i32 + margin.top as i32,
|
||||
// )
|
||||
// } else {
|
||||
// let empty_height =
|
||||
// (2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
|
||||
// (empty_height, empty_height)
|
||||
// }
|
||||
// }
|
||||
|
||||
fn preferred_height_for_width(&self, _width: i32) -> (i32, i32) {
|
||||
self.preferred_height()
|
||||
}
|
||||
// fn preferred_height_for_width(&self, _width: i32) -> (i32, i32) {
|
||||
// self.preferred_height()
|
||||
// }
|
||||
|
||||
fn draw(&self, cr: &cairo::Context) -> glib::Propagation {
|
||||
let res: Result<()> = (|| {
|
||||
let value = *self.value.borrow();
|
||||
let start_at = *self.start_at.borrow();
|
||||
let thickness = *self.thickness.borrow();
|
||||
let clockwise = *self.clockwise.borrow();
|
||||
// fn draw(&self, cr: &cairo::Context) -> glib::Propagation {
|
||||
// let res: Result<()> = (|| {
|
||||
// let value = *self.value.borrow();
|
||||
// let start_at = *self.start_at.borrow();
|
||||
// let thickness = *self.thickness.borrow();
|
||||
// let clockwise = *self.clockwise.borrow();
|
||||
|
||||
let styles = self.obj().style_context();
|
||||
let margin = styles.margin(gtk::StateFlags::NORMAL);
|
||||
// Padding is not supported yet
|
||||
let fg_color: gdk::RGBA = styles.color(gtk::StateFlags::NORMAL);
|
||||
let bg_color: gdk::RGBA = styles
|
||||
.style_property_for_state("background-color", gtk::StateFlags::NORMAL)
|
||||
.get()?;
|
||||
let (start_angle, end_angle) = if clockwise {
|
||||
(0.0, perc_to_rad(value))
|
||||
} else {
|
||||
(perc_to_rad(100.0 - value), 2f64 * std::f64::consts::PI)
|
||||
};
|
||||
// let styles = self.obj().style_context();
|
||||
// let margin = styles.margin(gtk4::StateFlags::NORMAL);
|
||||
// // Padding is not supported yet
|
||||
// let fg_color: gdk::RGBA = styles.color(gtk4::StateFlags::NORMAL);
|
||||
// let bg_color: gdk::RGBA = styles
|
||||
// .style_property_for_state("background-color", gtk4::StateFlags::NORMAL)
|
||||
// .get()?;
|
||||
// let (start_angle, end_angle) = if clockwise {
|
||||
// (0.0, perc_to_rad(value))
|
||||
// } else {
|
||||
// (perc_to_rad(100.0 - value), 2f64 * std::f64::consts::PI)
|
||||
// };
|
||||
|
||||
let total_width = self.obj().allocated_width() as f64;
|
||||
let total_height = self.obj().allocated_height() as f64;
|
||||
let center = (total_width / 2.0, total_height / 2.0);
|
||||
// let total_width = self.obj().allocated_width() as f64;
|
||||
// let total_height = self.obj().allocated_height() as f64;
|
||||
// let center = (total_width / 2.0, total_height / 2.0);
|
||||
|
||||
let circle_width = total_width - margin.left as f64 - margin.right as f64;
|
||||
let circle_height = total_height - margin.top as f64 - margin.bottom as f64;
|
||||
let outer_ring = f64::min(circle_width, circle_height) / 2.0;
|
||||
let inner_ring = (f64::min(circle_width, circle_height) / 2.0) - thickness;
|
||||
// let circle_width = total_width - margin.left as f64 - margin.right as f64;
|
||||
// let circle_height = total_height - margin.top as f64 - margin.bottom as f64;
|
||||
// let outer_ring = f64::min(circle_width, circle_height) / 2.0;
|
||||
// let inner_ring = (f64::min(circle_width, circle_height) / 2.0) - thickness;
|
||||
|
||||
cr.save()?;
|
||||
// cr.save()?;
|
||||
|
||||
// Centering
|
||||
cr.translate(center.0, center.1);
|
||||
cr.rotate(perc_to_rad(start_at));
|
||||
cr.translate(-center.0, -center.1);
|
||||
// // Centering
|
||||
// cr.translate(center.0, center.1);
|
||||
// cr.rotate(perc_to_rad(start_at));
|
||||
// cr.translate(-center.0, -center.1);
|
||||
|
||||
// Background Ring
|
||||
cr.move_to(center.0, center.1);
|
||||
cr.arc(center.0, center.1, outer_ring, 0.0, perc_to_rad(100.0));
|
||||
cr.set_source_rgba(bg_color.red(), bg_color.green(), bg_color.blue(), bg_color.alpha());
|
||||
cr.move_to(center.0, center.1);
|
||||
cr.arc(center.0, center.1, inner_ring, 0.0, perc_to_rad(100.0));
|
||||
cr.set_fill_rule(cairo::FillRule::EvenOdd); // Substract one circle from the other
|
||||
cr.fill()?;
|
||||
// // Background Ring
|
||||
// cr.move_to(center.0, center.1);
|
||||
// cr.arc(center.0, center.1, outer_ring, 0.0, perc_to_rad(100.0));
|
||||
// cr.set_source_rgba(bg_color.red(), bg_color.green(), bg_color.blue(), bg_color.alpha());
|
||||
// cr.move_to(center.0, center.1);
|
||||
// cr.arc(center.0, center.1, inner_ring, 0.0, perc_to_rad(100.0));
|
||||
// cr.set_fill_rule(cairo::FillRule::EvenOdd); // Substract one circle from the other
|
||||
// cr.fill()?;
|
||||
|
||||
// Foreground Ring
|
||||
cr.move_to(center.0, center.1);
|
||||
cr.arc(center.0, center.1, outer_ring, start_angle, end_angle);
|
||||
cr.set_source_rgba(fg_color.red(), fg_color.green(), fg_color.blue(), fg_color.alpha());
|
||||
cr.move_to(center.0, center.1);
|
||||
cr.arc(center.0, center.1, inner_ring, start_angle, end_angle);
|
||||
cr.set_fill_rule(cairo::FillRule::EvenOdd); // Substract one circle from the other
|
||||
cr.fill()?;
|
||||
cr.restore()?;
|
||||
// // Foreground Ring
|
||||
// cr.move_to(center.0, center.1);
|
||||
// cr.arc(center.0, center.1, outer_ring, start_angle, end_angle);
|
||||
// cr.set_source_rgba(fg_color.red(), fg_color.green(), fg_color.blue(), fg_color.alpha());
|
||||
// cr.move_to(center.0, center.1);
|
||||
// cr.arc(center.0, center.1, inner_ring, start_angle, end_angle);
|
||||
// cr.set_fill_rule(cairo::FillRule::EvenOdd); // Substract one circle from the other
|
||||
// cr.fill()?;
|
||||
// cr.restore()?;
|
||||
|
||||
// Draw the children widget, clipping it to the inside
|
||||
if let Some(child) = &*self.content.borrow() {
|
||||
cr.save()?;
|
||||
// // Draw the children widget, clipping it to the inside
|
||||
// if let Some(child) = &*self.content.borrow() {
|
||||
// cr.save()?;
|
||||
|
||||
// Center circular clip
|
||||
cr.arc(center.0, center.1, inner_ring + 1.0, 0.0, perc_to_rad(100.0));
|
||||
cr.set_source_rgba(bg_color.red(), 0.0, 0.0, bg_color.alpha());
|
||||
cr.clip();
|
||||
// // Center circular clip
|
||||
// cr.arc(center.0, center.1, inner_ring + 1.0, 0.0, perc_to_rad(100.0));
|
||||
// cr.set_source_rgba(bg_color.red(), 0.0, 0.0, bg_color.alpha());
|
||||
// cr.clip();
|
||||
|
||||
// Children widget
|
||||
self.obj().propagate_draw(child, cr);
|
||||
// // Children widget
|
||||
// self.obj().propagate_draw(child, cr);
|
||||
|
||||
cr.reset_clip();
|
||||
cr.restore()?;
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
// cr.reset_clip();
|
||||
// cr.restore()?;
|
||||
// }
|
||||
// Ok(())
|
||||
// })();
|
||||
|
||||
if let Err(error) = res {
|
||||
error_handling_ctx::print_error(error)
|
||||
};
|
||||
// if let Err(error) = res {
|
||||
// error_handling_ctx::print_error(error)
|
||||
// };
|
||||
|
||||
glib::Propagation::Proceed
|
||||
}
|
||||
}
|
||||
// glib::Propagation::Proceed
|
||||
// }
|
||||
// }
|
||||
|
||||
fn perc_to_rad(n: f64) -> f64 {
|
||||
(n / 100f64) * 2f64 * std::f64::consts::PI
|
||||
}
|
||||
// fn perc_to_rad(n: f64) -> f64 {
|
||||
// (n / 100f64) * 2f64 * std::f64::consts::PI
|
||||
// }
|
||||
|
||||
@@ -1,349 +1,349 @@
|
||||
use std::{cell::RefCell, collections::VecDeque};
|
||||
// https://www.figuiere.net/technotes/notes/tn002/
|
||||
// https://github.com/gtk-rs/examples/blob/master/src/bin/listbox_model.rs
|
||||
use anyhow::{anyhow, Result};
|
||||
use gtk::glib::{self, object_subclass, wrapper, Properties};
|
||||
use gtk::{cairo, gdk, prelude::*, subclass::prelude::*};
|
||||
// use std::{cell::RefCell, collections::VecDeque};
|
||||
// // https://www.figuiere.net/technotes/notes/tn002/
|
||||
// // https://github.com/gtk-rs/examples/blob/master/src/bin/listbox_model.rs
|
||||
// use anyhow::{anyhow, Result};
|
||||
// use gtk4::glib::{self, object_subclass, wrapper, Properties};
|
||||
// use gtk4::{cairo, gdk, prelude::*, subclass::prelude::*};
|
||||
|
||||
use crate::error_handling_ctx;
|
||||
// use crate::error_handling_ctx;
|
||||
|
||||
// This widget shouldn't be a Bin/Container but I've not been
|
||||
// able to subclass just a gtk::Widget
|
||||
wrapper! {
|
||||
pub struct Graph(ObjectSubclass<GraphPriv>)
|
||||
@extends gtk::Bin, gtk::Container, gtk::Widget;
|
||||
}
|
||||
// // This widget shouldn't be a Bin/Container but I've not been
|
||||
// // able to subclass just a gtk4::Widget
|
||||
// wrapper! {
|
||||
// pub struct Graph(ObjectSubclass<GraphPriv>)
|
||||
// @extends gtk4::Bin, gtk4::Container, gtk4::Widget;
|
||||
// }
|
||||
|
||||
#[derive(Properties)]
|
||||
#[properties(wrapper_type = Graph)]
|
||||
pub struct GraphPriv {
|
||||
#[property(get, set, nick = "Value", blurb = "The value", minimum = 0f64, maximum = f64::MAX, default = 0f64)]
|
||||
value: RefCell<f64>,
|
||||
// #[derive(Properties)]
|
||||
// #[properties(wrapper_type = Graph)]
|
||||
// pub struct GraphPriv {
|
||||
// #[property(get, set, nick = "Value", blurb = "The value", minimum = 0f64, maximum = f64::MAX, default = 0f64)]
|
||||
// value: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Thickness", blurb = "The Thickness", minimum = 0f64, maximum = f64::MAX, default = 1f64)]
|
||||
thickness: RefCell<f64>,
|
||||
// #[property(get, set, nick = "Thickness", blurb = "The Thickness", minimum = 0f64, maximum = f64::MAX, default = 1f64)]
|
||||
// thickness: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Line Style", blurb = "The Line Style", default = "miter")]
|
||||
line_style: RefCell<String>,
|
||||
// #[property(get, set, nick = "Line Style", blurb = "The Line Style", default = "miter")]
|
||||
// line_style: RefCell<String>,
|
||||
|
||||
#[property(get, set, nick = "Maximum Value", blurb = "The Maximum Value", minimum = 0f64, maximum = f64::MAX, default = 100f64)]
|
||||
min: RefCell<f64>,
|
||||
// #[property(get, set, nick = "Maximum Value", blurb = "The Maximum Value", minimum = 0f64, maximum = f64::MAX, default = 100f64)]
|
||||
// min: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Minumum Value", blurb = "The Minimum Value", minimum = 0f64, maximum = f64::MAX, default = 0f64)]
|
||||
max: RefCell<f64>,
|
||||
// #[property(get, set, nick = "Minumum Value", blurb = "The Minimum Value", minimum = 0f64, maximum = f64::MAX, default = 0f64)]
|
||||
// max: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Dynamic", blurb = "If it is dynamic", default = true)]
|
||||
dynamic: RefCell<bool>,
|
||||
// #[property(get, set, nick = "Dynamic", blurb = "If it is dynamic", default = true)]
|
||||
// dynamic: RefCell<bool>,
|
||||
|
||||
#[property(get, set, nick = "Time Range", blurb = "The Time Range", minimum = 0u64, maximum = u64::MAX, default = 10u64)]
|
||||
time_range: RefCell<u64>,
|
||||
// #[property(get, set, nick = "Time Range", blurb = "The Time Range", minimum = 0u64, maximum = u64::MAX, default = 10u64)]
|
||||
// time_range: RefCell<u64>,
|
||||
|
||||
#[property(get, set, nick = "Flip X", blurb = "Flip the x axis", default = true)]
|
||||
flip_x: RefCell<bool>,
|
||||
#[property(get, set, nick = "Flip Y", blurb = "Flip the y axis", default = true)]
|
||||
flip_y: RefCell<bool>,
|
||||
#[property(get, set, nick = "Vertical", blurb = "Exchange the x and y axes", default = false)]
|
||||
vertical: RefCell<bool>,
|
||||
// #[property(get, set, nick = "Flip X", blurb = "Flip the x axis", default = true)]
|
||||
// flip_x: RefCell<bool>,
|
||||
// #[property(get, set, nick = "Flip Y", blurb = "Flip the y axis", default = true)]
|
||||
// flip_y: RefCell<bool>,
|
||||
// #[property(get, set, nick = "Vertical", blurb = "Exchange the x and y axes", default = false)]
|
||||
// vertical: RefCell<bool>,
|
||||
|
||||
history: RefCell<VecDeque<(std::time::Instant, f64)>>,
|
||||
extra_point: RefCell<Option<(std::time::Instant, f64)>>,
|
||||
last_updated_at: RefCell<std::time::Instant>,
|
||||
}
|
||||
// history: RefCell<VecDeque<(std::time::Instant, f64)>>,
|
||||
// extra_point: RefCell<Option<(std::time::Instant, f64)>>,
|
||||
// last_updated_at: RefCell<std::time::Instant>,
|
||||
// }
|
||||
|
||||
impl Default for GraphPriv {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: RefCell::new(0.0),
|
||||
thickness: RefCell::new(1.0),
|
||||
line_style: RefCell::new("miter".to_string()),
|
||||
min: RefCell::new(0.0),
|
||||
max: RefCell::new(100.0),
|
||||
dynamic: RefCell::new(true),
|
||||
time_range: RefCell::new(10),
|
||||
flip_x: RefCell::new(true),
|
||||
flip_y: RefCell::new(true),
|
||||
vertical: RefCell::new(false),
|
||||
history: RefCell::new(VecDeque::new()),
|
||||
extra_point: RefCell::new(None),
|
||||
last_updated_at: RefCell::new(std::time::Instant::now()),
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl Default for GraphPriv {
|
||||
// fn default() -> Self {
|
||||
// Self {
|
||||
// value: RefCell::new(0.0),
|
||||
// thickness: RefCell::new(1.0),
|
||||
// line_style: RefCell::new("miter".to_string()),
|
||||
// min: RefCell::new(0.0),
|
||||
// max: RefCell::new(100.0),
|
||||
// dynamic: RefCell::new(true),
|
||||
// time_range: RefCell::new(10),
|
||||
// flip_x: RefCell::new(true),
|
||||
// flip_y: RefCell::new(true),
|
||||
// vertical: RefCell::new(false),
|
||||
// history: RefCell::new(VecDeque::new()),
|
||||
// extra_point: RefCell::new(None),
|
||||
// last_updated_at: RefCell::new(std::time::Instant::now()),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl GraphPriv {
|
||||
// Updates the history, removing points ouside the range
|
||||
fn update_history(&self, v: (std::time::Instant, f64)) {
|
||||
let mut history = self.history.borrow_mut();
|
||||
let mut last_value = self.extra_point.borrow_mut();
|
||||
let mut last_updated_at = self.last_updated_at.borrow_mut();
|
||||
*last_updated_at = std::time::Instant::now();
|
||||
// impl GraphPriv {
|
||||
// // Updates the history, removing points ouside the range
|
||||
// fn update_history(&self, v: (std::time::Instant, f64)) {
|
||||
// let mut history = self.history.borrow_mut();
|
||||
// let mut last_value = self.extra_point.borrow_mut();
|
||||
// let mut last_updated_at = self.last_updated_at.borrow_mut();
|
||||
// *last_updated_at = std::time::Instant::now();
|
||||
|
||||
while let Some(entry) = history.front() {
|
||||
if last_updated_at.duration_since(entry.0).as_millis() as u64
|
||||
> *self.time_range.borrow()
|
||||
{
|
||||
*last_value = history.pop_front();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
history.push_back(v);
|
||||
}
|
||||
/**
|
||||
* Receives normalized (0-1) coordinates `x` and `y` and convert them to the
|
||||
* point on the widget.
|
||||
*/
|
||||
fn value_to_point(&self, width: f64, height: f64, x: f64, y: f64) -> (f64, f64) {
|
||||
let x = if *self.flip_x.borrow() { 1.0 - x } else { x };
|
||||
let y = if *self.flip_y.borrow() { 1.0 - y } else { y };
|
||||
let (x, y) = if *self.vertical.borrow() { (y, x) } else { (x, y) };
|
||||
(width * x, height * y)
|
||||
}
|
||||
}
|
||||
// while let Some(entry) = history.front() {
|
||||
// if last_updated_at.duration_since(entry.0).as_millis() as u64
|
||||
// > *self.time_range.borrow()
|
||||
// {
|
||||
// *last_value = history.pop_front();
|
||||
// } else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// history.push_back(v);
|
||||
// }
|
||||
// /**
|
||||
// * Receives normalized (0-1) coordinates `x` and `y` and convert them to the
|
||||
// * point on the widget.
|
||||
// */
|
||||
// fn value_to_point(&self, width: f64, height: f64, x: f64, y: f64) -> (f64, f64) {
|
||||
// let x = if *self.flip_x.borrow() { 1.0 - x } else { x };
|
||||
// let y = if *self.flip_y.borrow() { 1.0 - y } else { y };
|
||||
// let (x, y) = if *self.vertical.borrow() { (y, x) } else { (x, y) };
|
||||
// (width * x, height * y)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ObjectImpl for GraphPriv {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
Self::derived_properties()
|
||||
}
|
||||
// impl ObjectImpl for GraphPriv {
|
||||
// fn properties() -> &'static [glib::ParamSpec] {
|
||||
// Self::derived_properties()
|
||||
// }
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"value" => {
|
||||
let value = value.get().unwrap();
|
||||
self.value.replace(value);
|
||||
self.update_history((std::time::Instant::now(), value));
|
||||
self.obj().queue_draw();
|
||||
}
|
||||
"thickness" => {
|
||||
self.thickness.replace(value.get().unwrap());
|
||||
}
|
||||
"max" => {
|
||||
self.max.replace(value.get().unwrap());
|
||||
}
|
||||
"min" => {
|
||||
self.min.replace(value.get().unwrap());
|
||||
}
|
||||
"dynamic" => {
|
||||
self.dynamic.replace(value.get().unwrap());
|
||||
}
|
||||
"time-range" => {
|
||||
self.time_range.replace(value.get().unwrap());
|
||||
}
|
||||
"line-style" => {
|
||||
self.line_style.replace(value.get().unwrap());
|
||||
}
|
||||
"flip-x" => {
|
||||
self.flip_x.replace(value.get().unwrap());
|
||||
}
|
||||
"flip-y" => {
|
||||
self.flip_y.replace(value.get().unwrap());
|
||||
}
|
||||
"vertical" => {
|
||||
self.vertical.replace(value.get().unwrap());
|
||||
}
|
||||
x => panic!("Tried to set inexistant property of Graph: {}", x,),
|
||||
}
|
||||
}
|
||||
// fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
// match pspec.name() {
|
||||
// "value" => {
|
||||
// let value = value.get().unwrap();
|
||||
// self.value.replace(value);
|
||||
// self.update_history((std::time::Instant::now(), value));
|
||||
// self.obj().queue_draw();
|
||||
// }
|
||||
// "thickness" => {
|
||||
// self.thickness.replace(value.get().unwrap());
|
||||
// }
|
||||
// "max" => {
|
||||
// self.max.replace(value.get().unwrap());
|
||||
// }
|
||||
// "min" => {
|
||||
// self.min.replace(value.get().unwrap());
|
||||
// }
|
||||
// "dynamic" => {
|
||||
// self.dynamic.replace(value.get().unwrap());
|
||||
// }
|
||||
// "time-range" => {
|
||||
// self.time_range.replace(value.get().unwrap());
|
||||
// }
|
||||
// "line-style" => {
|
||||
// self.line_style.replace(value.get().unwrap());
|
||||
// }
|
||||
// "flip-x" => {
|
||||
// self.flip_x.replace(value.get().unwrap());
|
||||
// }
|
||||
// "flip-y" => {
|
||||
// self.flip_y.replace(value.get().unwrap());
|
||||
// }
|
||||
// "vertical" => {
|
||||
// self.vertical.replace(value.get().unwrap());
|
||||
// }
|
||||
// x => panic!("Tried to set inexistant property of Graph: {}", x,),
|
||||
// }
|
||||
// }
|
||||
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.derived_property(id, pspec)
|
||||
}
|
||||
}
|
||||
// fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
// self.derived_property(id, pspec)
|
||||
// }
|
||||
// }
|
||||
|
||||
#[object_subclass]
|
||||
impl ObjectSubclass for GraphPriv {
|
||||
type ParentType = gtk::Bin;
|
||||
type Type = Graph;
|
||||
// #[object_subclass]
|
||||
// impl ObjectSubclass for GraphPriv {
|
||||
// type ParentType = gtk4::Bin;
|
||||
// type Type = Graph;
|
||||
|
||||
const NAME: &'static str = "Graph";
|
||||
// const NAME: &'static str = "Graph";
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.set_css_name("graph");
|
||||
}
|
||||
}
|
||||
// fn class_init(klass: &mut Self::Class) {
|
||||
// klass.set_css_name("graph");
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Default for Graph {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
// impl Default for Graph {
|
||||
// fn default() -> Self {
|
||||
// Self::new()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Graph {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new::<Self>()
|
||||
}
|
||||
}
|
||||
// impl Graph {
|
||||
// pub fn new() -> Self {
|
||||
// glib::Object::new::<Self>()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ContainerImpl for GraphPriv {
|
||||
fn add(&self, _widget: >k::Widget) {
|
||||
error_handling_ctx::print_error(anyhow!("Error, Graph widget shoudln't have any children"));
|
||||
}
|
||||
}
|
||||
// impl ContainerImpl for GraphPriv {
|
||||
// fn add(&self, _widget: >k4::Widget) {
|
||||
// error_handling_ctx::print_error(anyhow!("Error, Graph widget shoudln't have any children"));
|
||||
// }
|
||||
// }
|
||||
|
||||
impl BinImpl for GraphPriv {}
|
||||
impl WidgetImpl for GraphPriv {
|
||||
fn preferred_width(&self) -> (i32, i32) {
|
||||
let thickness = *self.thickness.borrow() as i32;
|
||||
(thickness, thickness)
|
||||
}
|
||||
// impl BinImpl for GraphPriv {}
|
||||
// impl WidgetImpl for GraphPriv {
|
||||
// fn preferred_width(&self) -> (i32, i32) {
|
||||
// let thickness = *self.thickness.borrow() as i32;
|
||||
// (thickness, thickness)
|
||||
// }
|
||||
|
||||
fn preferred_width_for_height(&self, height: i32) -> (i32, i32) {
|
||||
(height, height)
|
||||
}
|
||||
// fn preferred_width_for_height(&self, height: i32) -> (i32, i32) {
|
||||
// (height, height)
|
||||
// }
|
||||
|
||||
fn preferred_height(&self) -> (i32, i32) {
|
||||
let thickness = *self.thickness.borrow() as i32;
|
||||
(thickness, thickness)
|
||||
}
|
||||
// fn preferred_height(&self) -> (i32, i32) {
|
||||
// let thickness = *self.thickness.borrow() as i32;
|
||||
// (thickness, thickness)
|
||||
// }
|
||||
|
||||
fn preferred_height_for_width(&self, width: i32) -> (i32, i32) {
|
||||
(width, width)
|
||||
}
|
||||
// fn preferred_height_for_width(&self, width: i32) -> (i32, i32) {
|
||||
// (width, width)
|
||||
// }
|
||||
|
||||
fn draw(&self, cr: &cairo::Context) -> glib::Propagation {
|
||||
let res: Result<()> = (|| {
|
||||
let history = &*self.history.borrow();
|
||||
let extra_point = *self.extra_point.borrow();
|
||||
// fn draw(&self, cr: &cairo::Context) -> glib::Propagation {
|
||||
// let res: Result<()> = (|| {
|
||||
// let history = &*self.history.borrow();
|
||||
// let extra_point = *self.extra_point.borrow();
|
||||
|
||||
// Calculate the max value
|
||||
let (min, max) = {
|
||||
let mut max = *self.max.borrow();
|
||||
let min = *self.min.borrow();
|
||||
let dynamic = *self.dynamic.borrow();
|
||||
if dynamic {
|
||||
// Check for points higher than max
|
||||
for (_, value) in history {
|
||||
if *value > max {
|
||||
max = *value;
|
||||
}
|
||||
}
|
||||
if let Some((_, value)) = extra_point {
|
||||
if value > max {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
(min, max)
|
||||
};
|
||||
// // Calculate the max value
|
||||
// let (min, max) = {
|
||||
// let mut max = *self.max.borrow();
|
||||
// let min = *self.min.borrow();
|
||||
// let dynamic = *self.dynamic.borrow();
|
||||
// if dynamic {
|
||||
// // Check for points higher than max
|
||||
// for (_, value) in history {
|
||||
// if *value > max {
|
||||
// max = *value;
|
||||
// }
|
||||
// }
|
||||
// if let Some((_, value)) = extra_point {
|
||||
// if value > max {
|
||||
// max = value;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// (min, max)
|
||||
// };
|
||||
|
||||
let styles = self.obj().style_context();
|
||||
let (margin_top, margin_right, margin_bottom, margin_left) = {
|
||||
let margin = styles.margin(gtk::StateFlags::NORMAL);
|
||||
(margin.top as f64, margin.right as f64, margin.bottom as f64, margin.left as f64)
|
||||
};
|
||||
let width = self.obj().allocated_width() as f64 - margin_left - margin_right;
|
||||
let height = self.obj().allocated_height() as f64 - margin_top - margin_bottom;
|
||||
// let styles = self.obj().style_context();
|
||||
// let (margin_top, margin_right, margin_bottom, margin_left) = {
|
||||
// let margin = styles.margin(gtk4::StateFlags::NORMAL);
|
||||
// (margin.top as f64, margin.right as f64, margin.bottom as f64, margin.left as f64)
|
||||
// };
|
||||
// let width = self.obj().allocated_width() as f64 - margin_left - margin_right;
|
||||
// let height = self.obj().allocated_height() as f64 - margin_top - margin_bottom;
|
||||
|
||||
// Calculate graph points once
|
||||
// Separating this into another function would require pasing a
|
||||
// GraphPriv that would hide interior mutability
|
||||
let points = {
|
||||
let value_range = max - min;
|
||||
let time_range = *self.time_range.borrow() as f64;
|
||||
let last_updated_at = self.last_updated_at.borrow();
|
||||
let mut points = history
|
||||
.iter()
|
||||
.map(|(instant, value)| {
|
||||
let t = last_updated_at.duration_since(*instant).as_millis() as f64;
|
||||
self.value_to_point(
|
||||
width,
|
||||
height,
|
||||
t / time_range,
|
||||
(value - min) / value_range,
|
||||
)
|
||||
})
|
||||
.collect::<VecDeque<(f64, f64)>>();
|
||||
// // Calculate graph points once
|
||||
// // Separating this into another function would require pasing a
|
||||
// // GraphPriv that would hide interior mutability
|
||||
// let points = {
|
||||
// let value_range = max - min;
|
||||
// let time_range = *self.time_range.borrow() as f64;
|
||||
// let last_updated_at = self.last_updated_at.borrow();
|
||||
// let mut points = history
|
||||
// .iter()
|
||||
// .map(|(instant, value)| {
|
||||
// let t = last_updated_at.duration_since(*instant).as_millis() as f64;
|
||||
// self.value_to_point(
|
||||
// width,
|
||||
// height,
|
||||
// t / time_range,
|
||||
// (value - min) / value_range,
|
||||
// )
|
||||
// })
|
||||
// .collect::<VecDeque<(f64, f64)>>();
|
||||
|
||||
// Aad an extra point outside of the graph to extend the line to the left
|
||||
if let Some((instant, value)) = extra_point {
|
||||
let t = last_updated_at.duration_since(instant).as_millis() as f64;
|
||||
let (x, y) = self.value_to_point(
|
||||
width,
|
||||
height,
|
||||
(t - time_range) / time_range,
|
||||
(value - min) / value_range,
|
||||
);
|
||||
points.push_front(if *self.vertical.borrow() { (x, -y) } else { (-x, y) });
|
||||
}
|
||||
points
|
||||
};
|
||||
// // Aad an extra point outside of the graph to extend the line to the left
|
||||
// if let Some((instant, value)) = extra_point {
|
||||
// let t = last_updated_at.duration_since(instant).as_millis() as f64;
|
||||
// let (x, y) = self.value_to_point(
|
||||
// width,
|
||||
// height,
|
||||
// (t - time_range) / time_range,
|
||||
// (value - min) / value_range,
|
||||
// );
|
||||
// points.push_front(if *self.vertical.borrow() { (x, -y) } else { (-x, y) });
|
||||
// }
|
||||
// points
|
||||
// };
|
||||
|
||||
// Actually draw the graph
|
||||
cr.save()?;
|
||||
cr.translate(margin_left, margin_top);
|
||||
cr.rectangle(0.0, 0.0, width, height);
|
||||
cr.clip();
|
||||
// // Actually draw the graph
|
||||
// cr.save()?;
|
||||
// cr.translate(margin_left, margin_top);
|
||||
// cr.rectangle(0.0, 0.0, width, height);
|
||||
// cr.clip();
|
||||
|
||||
// Draw Background
|
||||
let bg_color: gdk::RGBA = styles
|
||||
.style_property_for_state("background-color", gtk::StateFlags::NORMAL)
|
||||
.get()?;
|
||||
if bg_color.alpha() > 0.0 {
|
||||
if let Some(first_point) = points.front() {
|
||||
cr.line_to(first_point.0, height + margin_bottom);
|
||||
}
|
||||
for (x, y) in points.iter() {
|
||||
cr.line_to(*x, *y);
|
||||
}
|
||||
cr.line_to(width, height);
|
||||
// // Draw Background
|
||||
// let bg_color: gdk::RGBA = styles
|
||||
// .style_property_for_state("background-color", gtk4::StateFlags::NORMAL)
|
||||
// .get()?;
|
||||
// if bg_color.alpha() > 0.0 {
|
||||
// if let Some(first_point) = points.front() {
|
||||
// cr.line_to(first_point.0, height + margin_bottom);
|
||||
// }
|
||||
// for (x, y) in points.iter() {
|
||||
// cr.line_to(*x, *y);
|
||||
// }
|
||||
// cr.line_to(width, height);
|
||||
|
||||
cr.set_source_rgba(
|
||||
bg_color.red(),
|
||||
bg_color.green(),
|
||||
bg_color.blue(),
|
||||
bg_color.alpha(),
|
||||
);
|
||||
cr.fill()?;
|
||||
}
|
||||
// cr.set_source_rgba(
|
||||
// bg_color.red(),
|
||||
// bg_color.green(),
|
||||
// bg_color.blue(),
|
||||
// bg_color.alpha(),
|
||||
// );
|
||||
// cr.fill()?;
|
||||
// }
|
||||
|
||||
// Draw Line
|
||||
let line_color: gdk::RGBA = styles.color(gtk::StateFlags::NORMAL);
|
||||
let thickness = *self.thickness.borrow();
|
||||
if line_color.alpha() > 0.0 && thickness > 0.0 {
|
||||
for (x, y) in points.iter() {
|
||||
cr.line_to(*x, *y);
|
||||
}
|
||||
// // Draw Line
|
||||
// let line_color: gdk::RGBA = styles.color(gtk4::StateFlags::NORMAL);
|
||||
// let thickness = *self.thickness.borrow();
|
||||
// if line_color.alpha() > 0.0 && thickness > 0.0 {
|
||||
// for (x, y) in points.iter() {
|
||||
// cr.line_to(*x, *y);
|
||||
// }
|
||||
|
||||
let line_style = &*self.line_style.borrow();
|
||||
apply_line_style(line_style.as_str(), cr)?;
|
||||
cr.set_line_width(thickness);
|
||||
cr.set_source_rgba(
|
||||
line_color.red(),
|
||||
line_color.green(),
|
||||
line_color.blue(),
|
||||
line_color.alpha(),
|
||||
);
|
||||
cr.stroke()?;
|
||||
}
|
||||
// let line_style = &*self.line_style.borrow();
|
||||
// apply_line_style(line_style.as_str(), cr)?;
|
||||
// cr.set_line_width(thickness);
|
||||
// cr.set_source_rgba(
|
||||
// line_color.red(),
|
||||
// line_color.green(),
|
||||
// line_color.blue(),
|
||||
// line_color.alpha(),
|
||||
// );
|
||||
// cr.stroke()?;
|
||||
// }
|
||||
|
||||
cr.reset_clip();
|
||||
cr.restore()?;
|
||||
Ok(())
|
||||
})();
|
||||
// cr.reset_clip();
|
||||
// cr.restore()?;
|
||||
// Ok(())
|
||||
// })();
|
||||
|
||||
if let Err(error) = res {
|
||||
error_handling_ctx::print_error(error)
|
||||
};
|
||||
// if let Err(error) = res {
|
||||
// error_handling_ctx::print_error(error)
|
||||
// };
|
||||
|
||||
glib::Propagation::Proceed
|
||||
}
|
||||
}
|
||||
// glib::Propagation::Proceed
|
||||
// }
|
||||
// }
|
||||
|
||||
fn apply_line_style(style: &str, cr: &cairo::Context) -> Result<()> {
|
||||
match style {
|
||||
"miter" => {
|
||||
cr.set_line_cap(cairo::LineCap::Butt);
|
||||
cr.set_line_join(cairo::LineJoin::Miter);
|
||||
}
|
||||
"bevel" => {
|
||||
cr.set_line_cap(cairo::LineCap::Square);
|
||||
cr.set_line_join(cairo::LineJoin::Bevel);
|
||||
}
|
||||
"round" => {
|
||||
cr.set_line_cap(cairo::LineCap::Round);
|
||||
cr.set_line_join(cairo::LineJoin::Round);
|
||||
}
|
||||
_ => Err(anyhow!("Error, the value: {} for atribute join is not valid", style))?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
// fn apply_line_style(style: &str, cr: &cairo::Context) -> Result<()> {
|
||||
// match style {
|
||||
// "miter" => {
|
||||
// cr.set_line_cap(cairo::LineCap::Butt);
|
||||
// cr.set_line_join(cairo::LineJoin::Miter);
|
||||
// }
|
||||
// "bevel" => {
|
||||
// cr.set_line_cap(cairo::LineCap::Square);
|
||||
// cr.set_line_join(cairo::LineJoin::Bevel);
|
||||
// }
|
||||
// "round" => {
|
||||
// cr.set_line_cap(cairo::LineCap::Round);
|
||||
// cr.set_line_join(cairo::LineJoin::Round);
|
||||
// }
|
||||
// _ => Err(anyhow!("Error, the value: {} for atribute join is not valid", style))?,
|
||||
// };
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
@@ -1,214 +1,214 @@
|
||||
use crate::window::coords::NumWithUnit;
|
||||
use anyhow::{anyhow, Result};
|
||||
use gtk::glib::{self, object_subclass, wrapper, Properties};
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
use std::{cell::RefCell, str::FromStr};
|
||||
// use crate::window::coords::NumWithUnit;
|
||||
// use anyhow::{anyhow, Result};
|
||||
// use gtk4::glib::{self, object_subclass, wrapper, Properties};
|
||||
// use gtk4::{prelude::*, subclass::prelude::*};
|
||||
// use std::{cell::RefCell, str::FromStr};
|
||||
|
||||
use crate::error_handling_ctx;
|
||||
// use crate::error_handling_ctx;
|
||||
|
||||
wrapper! {
|
||||
pub struct Transform(ObjectSubclass<TransformPriv>)
|
||||
@extends gtk::Bin, gtk::Container, gtk::Widget;
|
||||
}
|
||||
// wrapper! {
|
||||
// pub struct Transform(ObjectSubclass<TransformPriv>)
|
||||
// @extends gtk4::Bin, gtk4::Container, gtk4::Widget;
|
||||
// }
|
||||
|
||||
#[derive(Properties)]
|
||||
#[properties(wrapper_type = Transform)]
|
||||
pub struct TransformPriv {
|
||||
#[property(get, set, nick = "Rotate", blurb = "The Rotation", minimum = f64::MIN, maximum = f64::MAX, default = 0f64)]
|
||||
rotate: RefCell<f64>,
|
||||
// #[derive(Properties)]
|
||||
// #[properties(wrapper_type = Transform)]
|
||||
// pub struct TransformPriv {
|
||||
// #[property(get, set, nick = "Rotate", blurb = "The Rotation", minimum = f64::MIN, maximum = f64::MAX, default = 0f64)]
|
||||
// rotate: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Transform-Origin X", blurb = "X coordinate (%/px) for the Transform-Origin", default = None)]
|
||||
transform_origin_x: RefCell<Option<String>>,
|
||||
// #[property(get, set, nick = "Transform-Origin X", blurb = "X coordinate (%/px) for the Transform-Origin", default = None)]
|
||||
// transform_origin_x: RefCell<Option<String>>,
|
||||
|
||||
#[property(get, set, nick = "Transform-Origin Y", blurb = "Y coordinate (%/px) for the Transform-Origin", default = None)]
|
||||
transform_origin_y: RefCell<Option<String>>,
|
||||
// #[property(get, set, nick = "Transform-Origin Y", blurb = "Y coordinate (%/px) for the Transform-Origin", default = None)]
|
||||
// transform_origin_y: RefCell<Option<String>>,
|
||||
|
||||
#[property(get, set, nick = "Translate x", blurb = "The X Translation", default = None)]
|
||||
translate_x: RefCell<Option<String>>,
|
||||
// #[property(get, set, nick = "Translate x", blurb = "The X Translation", default = None)]
|
||||
// translate_x: RefCell<Option<String>>,
|
||||
|
||||
#[property(get, set, nick = "Translate y", blurb = "The Y Translation", default = None)]
|
||||
translate_y: RefCell<Option<String>>,
|
||||
// #[property(get, set, nick = "Translate y", blurb = "The Y Translation", default = None)]
|
||||
// translate_y: RefCell<Option<String>>,
|
||||
|
||||
#[property(get, set, nick = "Scale x", blurb = "The amount to scale in x", default = None)]
|
||||
scale_x: RefCell<Option<String>>,
|
||||
// #[property(get, set, nick = "Scale x", blurb = "The amount to scale in x", default = None)]
|
||||
// scale_x: RefCell<Option<String>>,
|
||||
|
||||
#[property(get, set, nick = "Scale y", blurb = "The amount to scale in y", default = None)]
|
||||
scale_y: RefCell<Option<String>>,
|
||||
// #[property(get, set, nick = "Scale y", blurb = "The amount to scale in y", default = None)]
|
||||
// scale_y: RefCell<Option<String>>,
|
||||
|
||||
content: RefCell<Option<gtk::Widget>>,
|
||||
}
|
||||
// content: RefCell<Option<gtk4::Widget>>,
|
||||
// }
|
||||
|
||||
// This should match the default values from the ParamSpecs
|
||||
impl Default for TransformPriv {
|
||||
fn default() -> Self {
|
||||
TransformPriv {
|
||||
rotate: RefCell::new(0.0),
|
||||
transform_origin_x: RefCell::new(None),
|
||||
transform_origin_y: RefCell::new(None),
|
||||
translate_x: RefCell::new(None),
|
||||
translate_y: RefCell::new(None),
|
||||
scale_x: RefCell::new(None),
|
||||
scale_y: RefCell::new(None),
|
||||
content: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
// // This should match the default values from the ParamSpecs
|
||||
// impl Default for TransformPriv {
|
||||
// fn default() -> Self {
|
||||
// TransformPriv {
|
||||
// rotate: RefCell::new(0.0),
|
||||
// transform_origin_x: RefCell::new(None),
|
||||
// transform_origin_y: RefCell::new(None),
|
||||
// translate_x: RefCell::new(None),
|
||||
// translate_y: RefCell::new(None),
|
||||
// scale_x: RefCell::new(None),
|
||||
// scale_y: RefCell::new(None),
|
||||
// content: RefCell::new(None),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ObjectImpl for TransformPriv {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
Self::derived_properties()
|
||||
}
|
||||
// impl ObjectImpl for TransformPriv {
|
||||
// fn properties() -> &'static [glib::ParamSpec] {
|
||||
// Self::derived_properties()
|
||||
// }
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"rotate" => {
|
||||
self.rotate.replace(value.get().unwrap());
|
||||
self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
}
|
||||
"transform-origin-x" => {
|
||||
self.transform_origin_x.replace(value.get().unwrap());
|
||||
self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
}
|
||||
"transform-origin-y" => {
|
||||
self.transform_origin_y.replace(value.get().unwrap());
|
||||
self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
}
|
||||
"translate-x" => {
|
||||
self.translate_x.replace(value.get().unwrap());
|
||||
self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
}
|
||||
"translate-y" => {
|
||||
self.translate_y.replace(value.get().unwrap());
|
||||
self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
}
|
||||
"scale-x" => {
|
||||
self.scale_x.replace(value.get().unwrap());
|
||||
self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
}
|
||||
"scale-y" => {
|
||||
self.scale_y.replace(value.get().unwrap());
|
||||
self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
}
|
||||
x => panic!("Tried to set inexistant property of Transform: {}", x,),
|
||||
}
|
||||
}
|
||||
// fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
// match pspec.name() {
|
||||
// "rotate" => {
|
||||
// self.rotate.replace(value.get().unwrap());
|
||||
// self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
// }
|
||||
// "transform-origin-x" => {
|
||||
// self.transform_origin_x.replace(value.get().unwrap());
|
||||
// self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
// }
|
||||
// "transform-origin-y" => {
|
||||
// self.transform_origin_y.replace(value.get().unwrap());
|
||||
// self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
// }
|
||||
// "translate-x" => {
|
||||
// self.translate_x.replace(value.get().unwrap());
|
||||
// self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
// }
|
||||
// "translate-y" => {
|
||||
// self.translate_y.replace(value.get().unwrap());
|
||||
// self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
// }
|
||||
// "scale-x" => {
|
||||
// self.scale_x.replace(value.get().unwrap());
|
||||
// self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
// }
|
||||
// "scale-y" => {
|
||||
// self.scale_y.replace(value.get().unwrap());
|
||||
// self.obj().queue_draw(); // Queue a draw call with the updated value
|
||||
// }
|
||||
// x => panic!("Tried to set inexistant property of Transform: {}", x,),
|
||||
// }
|
||||
// }
|
||||
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.derived_property(id, pspec)
|
||||
}
|
||||
}
|
||||
// fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
// self.derived_property(id, pspec)
|
||||
// }
|
||||
// }
|
||||
|
||||
#[object_subclass]
|
||||
impl ObjectSubclass for TransformPriv {
|
||||
type ParentType = gtk::Bin;
|
||||
type Type = Transform;
|
||||
// #[object_subclass]
|
||||
// impl ObjectSubclass for TransformPriv {
|
||||
// type ParentType = gtk4::Bin;
|
||||
// type Type = Transform;
|
||||
|
||||
const NAME: &'static str = "Transform";
|
||||
// const NAME: &'static str = "Transform";
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.set_css_name("transform");
|
||||
}
|
||||
}
|
||||
// fn class_init(klass: &mut Self::Class) {
|
||||
// klass.set_css_name("transform");
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Default for Transform {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
// impl Default for Transform {
|
||||
// fn default() -> Self {
|
||||
// Self::new()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Transform {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new::<Self>()
|
||||
}
|
||||
}
|
||||
// impl Transform {
|
||||
// pub fn new() -> Self {
|
||||
// glib::Object::new::<Self>()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ContainerImpl for TransformPriv {
|
||||
fn add(&self, widget: >k::Widget) {
|
||||
if let Some(content) = &*self.content.borrow() {
|
||||
// TODO: Handle this error when populating children widgets instead
|
||||
error_handling_ctx::print_error(anyhow!(
|
||||
"Error, trying to add multiple children to a circular-progress widget"
|
||||
));
|
||||
self.parent_remove(content);
|
||||
}
|
||||
self.parent_add(widget);
|
||||
self.content.replace(Some(widget.clone()));
|
||||
}
|
||||
}
|
||||
// impl ContainerImpl for TransformPriv {
|
||||
// fn add(&self, widget: >k4::Widget) {
|
||||
// if let Some(content) = &*self.content.borrow() {
|
||||
// // TODO: Handle this error when populating children widgets instead
|
||||
// error_handling_ctx::print_error(anyhow!(
|
||||
// "Error, trying to add multiple children to a circular-progress widget"
|
||||
// ));
|
||||
// self.parent_remove(content);
|
||||
// }
|
||||
// self.parent_add(widget);
|
||||
// self.content.replace(Some(widget.clone()));
|
||||
// }
|
||||
// }
|
||||
|
||||
impl BinImpl for TransformPriv {}
|
||||
impl WidgetImpl for TransformPriv {
|
||||
fn draw(&self, cr: >k::cairo::Context) -> glib::Propagation {
|
||||
let res: Result<()> = (|| {
|
||||
let rotate = *self.rotate.borrow();
|
||||
let total_width = self.obj().allocated_width() as f64;
|
||||
let total_height = self.obj().allocated_height() as f64;
|
||||
// impl BinImpl for TransformPriv {}
|
||||
// impl WidgetImpl for TransformPriv {
|
||||
// fn draw(&self, cr: >k4::cairo::Context) -> glib::Propagation {
|
||||
// let res: Result<()> = (|| {
|
||||
// let rotate = *self.rotate.borrow();
|
||||
// let total_width = self.obj().allocated_width() as f64;
|
||||
// let total_height = self.obj().allocated_height() as f64;
|
||||
|
||||
cr.save()?;
|
||||
// cr.save()?;
|
||||
|
||||
let transform_origin_x = match &*self.transform_origin_x.borrow() {
|
||||
Some(rcx) => {
|
||||
NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
let transform_origin_y = match &*self.transform_origin_y.borrow() {
|
||||
Some(rcy) => {
|
||||
NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
// let transform_origin_x = match &*self.transform_origin_x.borrow() {
|
||||
// Some(rcx) => {
|
||||
// NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64
|
||||
// }
|
||||
// None => 0.0,
|
||||
// };
|
||||
// let transform_origin_y = match &*self.transform_origin_y.borrow() {
|
||||
// Some(rcy) => {
|
||||
// NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64
|
||||
// }
|
||||
// None => 0.0,
|
||||
// };
|
||||
|
||||
let translate_x = match &*self.translate_x.borrow() {
|
||||
Some(tx) => {
|
||||
NumWithUnit::from_str(tx)?.pixels_relative_to(total_width as i32) as f64
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
// let translate_x = match &*self.translate_x.borrow() {
|
||||
// Some(tx) => {
|
||||
// NumWithUnit::from_str(tx)?.pixels_relative_to(total_width as i32) as f64
|
||||
// }
|
||||
// None => 0.0,
|
||||
// };
|
||||
|
||||
let translate_y = match &*self.translate_y.borrow() {
|
||||
Some(ty) => {
|
||||
NumWithUnit::from_str(ty)?.pixels_relative_to(total_height as i32) as f64
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
// let translate_y = match &*self.translate_y.borrow() {
|
||||
// Some(ty) => {
|
||||
// NumWithUnit::from_str(ty)?.pixels_relative_to(total_height as i32) as f64
|
||||
// }
|
||||
// None => 0.0,
|
||||
// };
|
||||
|
||||
let scale_x = match &*self.scale_x.borrow() {
|
||||
Some(sx) => {
|
||||
NumWithUnit::from_str(sx)?.perc_relative_to(total_width as i32) as f64 / 100.0
|
||||
}
|
||||
None => 1.0,
|
||||
};
|
||||
// let scale_x = match &*self.scale_x.borrow() {
|
||||
// Some(sx) => {
|
||||
// NumWithUnit::from_str(sx)?.perc_relative_to(total_width as i32) as f64 / 100.0
|
||||
// }
|
||||
// None => 1.0,
|
||||
// };
|
||||
|
||||
let scale_y = match &*self.scale_y.borrow() {
|
||||
Some(sy) => {
|
||||
NumWithUnit::from_str(sy)?.perc_relative_to(total_height as i32) as f64 / 100.0
|
||||
}
|
||||
None => 1.0,
|
||||
};
|
||||
// let scale_y = match &*self.scale_y.borrow() {
|
||||
// Some(sy) => {
|
||||
// NumWithUnit::from_str(sy)?.perc_relative_to(total_height as i32) as f64 / 100.0
|
||||
// }
|
||||
// None => 1.0,
|
||||
// };
|
||||
|
||||
cr.translate(transform_origin_x, transform_origin_y);
|
||||
cr.rotate(perc_to_rad(rotate));
|
||||
cr.translate(translate_x - transform_origin_x, translate_y - transform_origin_y);
|
||||
cr.scale(scale_x, scale_y);
|
||||
// cr.translate(transform_origin_x, transform_origin_y);
|
||||
// cr.rotate(perc_to_rad(rotate));
|
||||
// cr.translate(translate_x - transform_origin_x, translate_y - transform_origin_y);
|
||||
// cr.scale(scale_x, scale_y);
|
||||
|
||||
// Children widget
|
||||
if let Some(child) = &*self.content.borrow() {
|
||||
self.obj().propagate_draw(child, cr);
|
||||
}
|
||||
// // Children widget
|
||||
// if let Some(child) = &*self.content.borrow() {
|
||||
// self.obj().propagate_draw(child, cr);
|
||||
// }
|
||||
|
||||
cr.restore()?;
|
||||
Ok(())
|
||||
})();
|
||||
// cr.restore()?;
|
||||
// Ok(())
|
||||
// })();
|
||||
|
||||
if let Err(error) = res {
|
||||
error_handling_ctx::print_error(error)
|
||||
};
|
||||
// if let Err(error) = res {
|
||||
// error_handling_ctx::print_error(error)
|
||||
// };
|
||||
|
||||
glib::Propagation::Proceed
|
||||
}
|
||||
}
|
||||
// glib::Propagation::Proceed
|
||||
// }
|
||||
// }
|
||||
|
||||
fn perc_to_rad(n: f64) -> f64 {
|
||||
(n / 100f64) * 2f64 * std::f64::consts::PI
|
||||
}
|
||||
// fn perc_to_rad(n: f64) -> f64 {
|
||||
// (n / 100f64) * 2f64 * std::f64::consts::PI
|
||||
// }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
use crate::gtk::prelude::LabelExt;
|
||||
use crate::gtk4::prelude::LabelExt;
|
||||
use anyhow::{anyhow, Result};
|
||||
use gtk::pango;
|
||||
use gtk4::pango;
|
||||
use rhai::Map;
|
||||
use std::process::Command;
|
||||
|
||||
@@ -75,29 +75,29 @@ pub fn dynamic_eq(a: &rhai::Dynamic, b: &rhai::Dynamic) -> bool {
|
||||
}
|
||||
|
||||
/// ALL WIDGETS
|
||||
pub(super) fn parse_align(o: &str) -> Result<gtk::Align> {
|
||||
pub(super) fn parse_align(o: &str) -> Result<gtk4::Align> {
|
||||
match o.to_ascii_lowercase().as_str() {
|
||||
"fill" => Ok(gtk::Align::Fill),
|
||||
"baseline" => Ok(gtk::Align::Baseline),
|
||||
"center" => Ok(gtk::Align::Center),
|
||||
"start" => Ok(gtk::Align::Start),
|
||||
"end" => Ok(gtk::Align::End),
|
||||
"fill" => Ok(gtk4::Align::Fill),
|
||||
"baseline" => Ok(gtk4::Align::Baseline),
|
||||
"center" => Ok(gtk4::Align::Center),
|
||||
"start" => Ok(gtk4::Align::Start),
|
||||
"end" => Ok(gtk4::Align::End),
|
||||
other => Err(anyhow!("Invalid alignment: {}", other)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gtk Box
|
||||
pub(super) fn parse_orientation(ori: &str) -> Result<gtk::Orientation> {
|
||||
pub(super) fn parse_orientation(ori: &str) -> Result<gtk4::Orientation> {
|
||||
match ori.to_ascii_lowercase().as_str() {
|
||||
"h" | "horizontal" => Ok(gtk::Orientation::Horizontal),
|
||||
"v" | "vertical" => Ok(gtk::Orientation::Vertical),
|
||||
"h" | "horizontal" => Ok(gtk4::Orientation::Horizontal),
|
||||
"v" | "vertical" => Ok(gtk4::Orientation::Vertical),
|
||||
other => Err(anyhow!("Invalid orientation: {}", other)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gtk Label
|
||||
pub(super) fn apply_ellipsize_settings(
|
||||
label: >k::Label,
|
||||
label: >k4::Label,
|
||||
truncate: bool,
|
||||
limit_width: i32,
|
||||
truncate_left: bool,
|
||||
@@ -126,12 +126,12 @@ pub(super) fn parse_gravity(s: &str) -> Result<pango::Gravity> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_justification(s: &str) -> Result<gtk::Justification> {
|
||||
pub(super) fn parse_justification(s: &str) -> Result<gtk4::Justification> {
|
||||
match s.to_ascii_lowercase().as_str() {
|
||||
"left" => Ok(gtk::Justification::Left),
|
||||
"right" => Ok(gtk::Justification::Right),
|
||||
"center" => Ok(gtk::Justification::Center),
|
||||
"fill" => Ok(gtk::Justification::Fill),
|
||||
"left" => Ok(gtk4::Justification::Left),
|
||||
"right" => Ok(gtk4::Justification::Right),
|
||||
"center" => Ok(gtk4::Justification::Center),
|
||||
"fill" => Ok(gtk4::Justification::Fill),
|
||||
_ => Err(anyhow!("Invalid justification: '{}'", s)),
|
||||
}
|
||||
}
|
||||
@@ -146,12 +146,12 @@ pub(super) fn parse_wrap_mode(s: &str) -> Result<pango::WrapMode> {
|
||||
}
|
||||
|
||||
/// Gtk scale (slider)
|
||||
pub(super) fn parse_position_type(s: &str) -> Result<gtk::PositionType> {
|
||||
pub(super) fn parse_position_type(s: &str) -> Result<gtk4::PositionType> {
|
||||
match s.to_ascii_lowercase().as_str() {
|
||||
"left" => Ok(gtk::PositionType::Left),
|
||||
"right" => Ok(gtk::PositionType::Right),
|
||||
"top" => Ok(gtk::PositionType::Top),
|
||||
"bottom" => Ok(gtk::PositionType::Bottom),
|
||||
"left" => Ok(gtk4::PositionType::Left),
|
||||
"right" => Ok(gtk4::PositionType::Right),
|
||||
"top" => Ok(gtk4::PositionType::Top),
|
||||
"bottom" => Ok(gtk4::PositionType::Bottom),
|
||||
_ => Err(anyhow!("Invalid position type: '{}'", s)),
|
||||
}
|
||||
}
|
||||
@@ -172,28 +172,24 @@ where
|
||||
}
|
||||
|
||||
/// Revealer
|
||||
pub(super) fn parse_revealer_transition(t: &str) -> Result<gtk::RevealerTransitionType> {
|
||||
pub(super) fn parse_revealer_transition(t: &str) -> Result<gtk4::RevealerTransitionType> {
|
||||
match t.to_ascii_lowercase().as_str() {
|
||||
"slideright" => Ok(gtk::RevealerTransitionType::SlideRight),
|
||||
"slideleft" => Ok(gtk::RevealerTransitionType::SlideLeft),
|
||||
"slideup" => Ok(gtk::RevealerTransitionType::SlideUp),
|
||||
"slidedown" => Ok(gtk::RevealerTransitionType::SlideDown),
|
||||
"fade" | "crossfade" => Ok(gtk::RevealerTransitionType::Crossfade),
|
||||
"none" => Ok(gtk::RevealerTransitionType::None),
|
||||
"slideright" => Ok(gtk4::RevealerTransitionType::SlideRight),
|
||||
"slideleft" => Ok(gtk4::RevealerTransitionType::SlideLeft),
|
||||
"slideup" => Ok(gtk4::RevealerTransitionType::SlideUp),
|
||||
"slidedown" => Ok(gtk4::RevealerTransitionType::SlideDown),
|
||||
"fade" | "crossfade" => Ok(gtk4::RevealerTransitionType::Crossfade),
|
||||
"none" => Ok(gtk4::RevealerTransitionType::None),
|
||||
_ => Err(anyhow!("Invalid transition: '{}'", t)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gtk Image
|
||||
// icon-size - "menu", "small-toolbar", "toolbar", "large-toolbar", "button", "dnd", "dialog"
|
||||
pub(super) fn parse_icon_size(o: &str) -> Result<gtk::IconSize> {
|
||||
// icon-size - "normal", "large"
|
||||
pub(super) fn parse_icon_size(o: &str) -> Result<gtk4::IconSize> {
|
||||
match o.to_ascii_lowercase().as_str() {
|
||||
"menu" => Ok(gtk::IconSize::Menu),
|
||||
"small-toolbar" | "toolbar" => Ok(gtk::IconSize::SmallToolbar),
|
||||
"large-toolbar" => Ok(gtk::IconSize::LargeToolbar),
|
||||
"button" => Ok(gtk::IconSize::Button),
|
||||
"dnd" => Ok(gtk::IconSize::Dnd),
|
||||
"dialog" => Ok(gtk::IconSize::Dialog),
|
||||
"normal" => Ok(gtk4::IconSize::Normal),
|
||||
"large" => Ok(gtk4::IconSize::Large),
|
||||
_ => Err(anyhow!("Invalid icon size: '{}'", o)),
|
||||
}
|
||||
}
|
||||
@@ -215,14 +211,14 @@ pub(super) fn parse_dragtype(o: &str) -> Result<DragEntryType> {
|
||||
|
||||
/// Stack widget
|
||||
// transition - "slideright", "slideleft", "slideup", "slidedown", "crossfade", "none"
|
||||
pub(super) fn parse_stack_transition(t: &str) -> Result<gtk::StackTransitionType> {
|
||||
pub(super) fn parse_stack_transition(t: &str) -> Result<gtk4::StackTransitionType> {
|
||||
match t.to_ascii_lowercase().as_str() {
|
||||
"slideright" => Ok(gtk::StackTransitionType::SlideRight),
|
||||
"slideleft" => Ok(gtk::StackTransitionType::SlideLeft),
|
||||
"slideup" => Ok(gtk::StackTransitionType::SlideUp),
|
||||
"slidedown" => Ok(gtk::StackTransitionType::SlideDown),
|
||||
"fade" | "crossfade" => Ok(gtk::StackTransitionType::Crossfade),
|
||||
"none" => Ok(gtk::StackTransitionType::None),
|
||||
"slideright" => Ok(gtk4::StackTransitionType::SlideRight),
|
||||
"slideleft" => Ok(gtk4::StackTransitionType::SlideLeft),
|
||||
"slideup" => Ok(gtk4::StackTransitionType::SlideUp),
|
||||
"slidedown" => Ok(gtk4::StackTransitionType::SlideDown),
|
||||
"fade" | "crossfade" => Ok(gtk4::StackTransitionType::Crossfade),
|
||||
"none" => Ok(gtk4::StackTransitionType::None),
|
||||
_ => Err(anyhow!("Invalid stack transition: '{}'", t)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use gtk::glib::{self, object_subclass, wrapper, Properties};
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
use gtk4::glib::{self, object_subclass, wrapper, Properties};
|
||||
use gtk4::{prelude::*, subclass::prelude::*};
|
||||
use std::cell::RefCell;
|
||||
|
||||
wrapper! {
|
||||
pub struct Window(ObjectSubclass<WindowPriv>)
|
||||
@extends gtk::Window, gtk::Bin, gtk::Container, gtk::Widget, @implements gtk::Buildable;
|
||||
@extends gtk4::Window, gtk4::Bin, gtk4::Container, gtk4::Widget, @implements gtk4::Buildable;
|
||||
}
|
||||
|
||||
#[derive(Properties)]
|
||||
@@ -26,7 +26,7 @@ impl Default for WindowPriv {
|
||||
|
||||
#[object_subclass]
|
||||
impl ObjectSubclass for WindowPriv {
|
||||
type ParentType = gtk::Window;
|
||||
type ParentType = gtk4::Window;
|
||||
type Type = Window;
|
||||
|
||||
const NAME: &'static str = "WindowEwwii";
|
||||
@@ -38,15 +38,15 @@ impl Default for Window {
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(type_: gtk::WindowType, x_: i32, y_: i32) -> Self {
|
||||
let w: Self = glib::Object::builder().property("type", type_).build();
|
||||
let priv_ = w.imp();
|
||||
priv_.x.replace(x_);
|
||||
priv_.y.replace(y_);
|
||||
w
|
||||
}
|
||||
}
|
||||
// impl Window {
|
||||
// pub fn new(type_: gtk4::WindowType, x_: i32, y_: i32) -> Self {
|
||||
// let w: Self = glib::Object::builder().property("type", type_).build();
|
||||
// let priv_ = w.imp();
|
||||
// priv_.x.replace(x_);
|
||||
// priv_.y.replace(y_);
|
||||
// w
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ObjectImpl for WindowPriv {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
@@ -57,7 +57,7 @@ impl ObjectImpl for WindowPriv {
|
||||
self.derived_property(id, pspec)
|
||||
}
|
||||
}
|
||||
impl WindowImpl for WindowPriv {}
|
||||
impl BinImpl for WindowPriv {}
|
||||
impl ContainerImpl for WindowPriv {}
|
||||
impl WidgetImpl for WindowPriv {}
|
||||
// impl WindowImpl for WindowPriv {}
|
||||
// impl BinImpl for WindowPriv {}
|
||||
// impl ContainerImpl for WindowPriv {}
|
||||
// impl WidgetImpl for WindowPriv {}
|
||||
|
||||
@@ -13,7 +13,6 @@ shared_utils.workspace = true
|
||||
|
||||
rhai = { workspace = true, features = ["internals"] }
|
||||
anyhow.workspace = true
|
||||
gtk.workspace = true
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
log.workspace = true
|
||||
once_cell.workspace = true
|
||||
|
||||
Reference in New Issue
Block a user