From d5f9a1609d90b3d313adec7c005de1aa46824e1f Mon Sep 17 00:00:00 2001 From: Dave Patrick Caberto Date: Thu, 7 Dec 2023 14:13:58 +0800 Subject: [PATCH] different layout engine --- data/resources/ui/window.ui | 26 ++++++++++++--- src/graphviz.rs | 64 ++++++++++++++++++++++++++++++------- src/window.rs | 31 +++++++++++++++--- 3 files changed, 100 insertions(+), 21 deletions(-) diff --git a/data/resources/ui/window.ui b/data/resources/ui/window.ui index edc326a..2a3342a 100644 --- a/data/resources/ui/window.ui +++ b/data/resources/ui/window.ui @@ -55,12 +55,28 @@ - - - - 20 + + vertical + + + True + + + 20 + + - + + + + + + + + + diff --git a/src/graphviz.rs b/src/graphviz.rs index b98e630..c8083bb 100644 --- a/src/graphviz.rs +++ b/src/graphviz.rs @@ -2,34 +2,74 @@ use std::{io::Write, path::Path}; use anyhow::Result; use async_process::Command; +use gtk::glib::{self, translate::TryFromGlib}; use tempfile::NamedTempFile; +#[derive(Debug, Clone, Copy, glib::Enum)] +#[enum_type(name = "DaggerLayout")] +pub enum Layout { + Dot, + Neato, + Twopi, + Circo, + Fdp, + // Asage, + Patchwork, + Sfdp, +} + +impl TryFrom for Layout { + type Error = i32; + + fn try_from(val: i32) -> Result { + unsafe { Self::try_from_glib(val) } + } +} + +impl Layout { + fn as_arg(self) -> &'static str { + match self { + Self::Dot => "dot", + Self::Neato => "neato", + Self::Twopi => "twopi", + Self::Circo => "circo", + Self::Fdp => "fdp", + Self::Patchwork => "patchwork", + Self::Sfdp => "sfdp", + } + } +} + /// Generate a PNG from the given DOT contents. -pub async fn run_with_str(contents: &str) -> Result> { - let mut in_file = NamedTempFile::new()?; - in_file.write_all(contents.as_bytes())?; +pub async fn run_with_str(contents: &str, layout: Layout) -> Result> { + let mut input_file = NamedTempFile::new()?; + input_file.write_all(contents.as_bytes())?; - let in_path = in_file.into_temp_path(); + let input_path = input_file.into_temp_path(); - run(&in_path).await + run(&input_path, layout).await } /// Generate a PNG from the given DOT file. -pub async fn run(in_path: &Path) -> Result> { - let out_path = NamedTempFile::new()?.into_temp_path(); +pub async fn run(input_path: &Path, layout: Layout) -> Result> { + let output_path = NamedTempFile::new()?.into_temp_path(); + + let format = "png"; let child = Command::new("dot") + .arg(input_path) .arg("-T") - .arg("png") - .arg(in_path) + .arg(format) + .arg("-K") + .arg(layout.as_arg()) .arg("-o") - .arg(&out_path) + .arg(&output_path) .spawn()?; let output = child.output().await?; tracing::debug!(?output, "Child exited"); - let out_bytes = async_fs::read(&out_path).await?; + let output_bytes = async_fs::read(&output_path).await?; - Ok(out_bytes) + Ok(output_bytes) } diff --git a/src/window.rs b/src/window.rs index bbe2dd6..1557d3c 100644 --- a/src/window.rs +++ b/src/window.rs @@ -5,17 +5,17 @@ use anyhow::Result; use gettextrs::gettext; use gtk::{ gdk, gio, - glib::{self, clone}, + glib::{self, clone, closure}, }; use gtk_source::prelude::*; use crate::{ application::Application, config::{APP_ID, PROFILE}, - graphviz, + graphviz::{self, Layout}, }; -const DRAW_GRAPH_INTERVAL: Duration = Duration::from_millis(200); +const DRAW_GRAPH_INTERVAL: Duration = Duration::from_millis(100); mod imp { use std::cell::Cell; @@ -29,6 +29,8 @@ mod imp { pub(super) buffer: TemplateChild, #[template_child] pub(super) picture: TemplateChild, + #[template_child] + pub(super) layout_drop_down: TemplateChild, pub(super) queued_draw_graph: Cell, } @@ -74,6 +76,19 @@ mod imp { obj.queue_draw_graph(); })); + self.layout_drop_down.set_expression(Some( + >k::ClosureExpression::new::( + &[] as &[gtk::Expression], + closure!(|list_item: adw::EnumListItem| list_item.name()), + ), + )); + self.layout_drop_down + .set_model(Some(&adw::EnumListModel::new(Layout::static_type()))); + self.layout_drop_down + .connect_selected_notify(clone!(@weak obj => move |_| { + obj.queue_draw_graph(); + })); + glib::spawn_future_local(clone!(@weak obj => async move { obj.start_draw_graph_loop().await; })); @@ -209,7 +224,15 @@ impl Window { return Ok(None); } - let png_bytes = graphviz::run_with_str(&contents).await?; + let selected_item = imp + .layout_drop_down + .selected_item() + .unwrap() + .downcast::() + .unwrap(); + let selected_layout = Layout::try_from(selected_item.value()).unwrap(); + + let png_bytes = graphviz::run_with_str(&contents, selected_layout).await?; let texture = gdk::Texture::from_bytes(&glib::Bytes::from_owned(png_bytes))?; Ok(Some(texture)) }