Skip to content

Commit

Permalink
Implement context menu
Browse files Browse the repository at this point in the history
  • Loading branch information
salihgerdan committed Aug 1, 2023
1 parent adab969 commit de2a032
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 9 deletions.
9 changes: 9 additions & 0 deletions src/filetree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ impl Tree {
node = p;
}
}
pub fn get_full_path(&self, mut node: NodeID) -> String {
let mut filename = self.elems[node].name.to_string();
while let Some(p) = self.elems[node].parent {
let separator = if cfg!(unix) { "/" } else { "\\" };
filename = self.elems[p].name.to_string() + separator + filename.as_str();
node = p;
}
filename
}
pub fn add_elem(&mut self, parent: NodeID, name: String, is_file: bool, size: u64) {
self.last_id += 1;
let node = Node {
Expand Down
14 changes: 14 additions & 0 deletions src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ pub fn initiate_ui() {
let application = gtk::Application::new(Some(config::APP_NAME), ApplicationFlags::HANDLES_OPEN);
application.connect_open(build_ui);
application.connect_activate(|app| build_ui(app, &[], ""));

let action_show = gtk::gio::SimpleAction::new("show", Some(glib::VariantTy::STRING));
action_show.connect_activate(glib::clone!(@weak application => move |_, param| {
param.map(|x| {
gtk::show_uri(None::<&gtk::Window>, x.to_string().trim_matches('\''), 0);
//dbg!(gtk::gio::AppInfo::launch_default_for_uri(x.to_string().as_str(), None::<&gtk::gdk::AppLaunchContext>))
});
}));
let action_disabled = gtk::gio::SimpleAction::new("disabled", None);
action_disabled.set_enabled(false);

application.add_action(&action_show);
application.add_action(&action_disabled);

application.run();
}

Expand Down
83 changes: 75 additions & 8 deletions src/gui/treemap_widget/imp.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use crate::{
bytes_display,
filetree::{Node, NodeID, Tree},
node_color,
squarify::{self, GUINode},
utils,
};
use gtk::gdk::RGBA;
use gtk::glib;
use gtk::graphene::{Point, Rect};
use gtk::pango;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::PopoverMenu;
use gtk::Tooltip;
use once_cell::sync::Lazy;
use std::cell::RefCell;
Expand All @@ -27,6 +28,7 @@ pub struct TreeMapWidget {
pub thread_handle: RefCell<Option<JoinHandle<()>>>,
last_width: RefCell<f32>,
last_height: RefCell<f32>,
popover_menu: RefCell<Option<PopoverMenu>>,
}

#[glib::object_subclass]
Expand Down Expand Up @@ -79,12 +81,7 @@ fn query_tooltip(
let found_node = locate_node(&tree, root, &gui_node_map, x as f32, y as f32);
if let Some(node) = found_node {
tooltip.set_text(Some(
format!(
"{} ({})",
node.name,
bytes_display::bytes_display(node.size)
)
.as_str(),
format!("{} ({})", node.name, utils::bytes_display(node.size)).as_str(),
));
// unwrap okay, we found it already
let rect = gui_node_map.get(&node.id).unwrap().rect;
Expand All @@ -101,6 +98,57 @@ fn query_tooltip(
}
}

fn create_context_menu(widget: &super::TreeMapWidget, x: f64, y: f64) {
let imp = widget.imp();
let gui_node_map = imp.gui_node_map.borrow();
let tree_mutex = &imp.tree_mutex;
let (found_node, uri, parent_uri) = {
let tree = tree_mutex.lock().unwrap();
let root = tree.get_elem(0);
let found_node =
locate_node(&tree, root, &gui_node_map, x as f32, y as f32).map(|x| x.clone());
let uri = found_node
.as_ref()
.and_then(|node| glib::filename_to_uri(tree.get_full_path(node.id), None).ok());
let parent_uri = found_node
.as_ref()
.and_then(|node| node.parent)
.map(|node_id| tree.get_elem(node_id))
.and_then(|node| glib::filename_to_uri(tree.get_full_path(node.id), None).ok());
(found_node, uri, parent_uri)
};
if let (Some(node), Some(uri)) = (found_node, uri) {
if let Some(popover_menu) = imp.popover_menu.borrow().as_ref() {
popover_menu.set_pointing_to(Some(&gtk::gdk::Rectangle::new(x as i32, y as i32, 1, 1)));
let menu = gtk::gio::Menu::new();
let menu_item_name = gtk::gio::MenuItem::new(
Some(&utils::abbreviate_string(&node.name, 15)),
Some("app.disabled"),
);
menu.append_item(&menu_item_name);
let open_action_name = format!("app.show(\"{}\")", uri);
let menu_item_open =
gtk::gio::MenuItem::new(Some("Open"), Some(open_action_name.as_str()));
menu.append_item(&menu_item_open);

if node.is_file {
if let Some(parent_uri) = parent_uri {
let dir_action_name = format!("app.show(\"{}\")", parent_uri);
let menu_item_dir = gtk::gio::MenuItem::new(
Some("Show directory"),
Some(dir_action_name.as_str()),
);
menu.append_item(&menu_item_dir);
}
}

popover_menu.set_menu_model(Some(&menu));

popover_menu.show();
}
}
}

// try making this an associated function
fn refresh(widget: &super::TreeMapWidget) -> Continue {
let imp = widget.imp();
Expand Down Expand Up @@ -154,6 +202,22 @@ impl ObjectImpl for TreeMapWidget {
obj.set_height_request(100);
obj.set_has_tooltip(true);
obj.connect_query_tooltip(query_tooltip);
self.popover_menu.replace(Some({
let menu = PopoverMenu::from_model(None::<&gtk::gio::MenuModel>);
menu.set_parent(&*obj);
menu.set_has_arrow(false);
menu.set_halign(gtk::Align::Start);
menu
}));

let right_click = gtk::GestureClick::new();
right_click.set_button(gtk::gdk::ffi::GDK_BUTTON_SECONDARY as u32);
right_click.connect_pressed(glib::clone!(@weak obj => move |right_click, _, x, y| {
right_click.set_state(gtk::EventSequenceState::Claimed);
create_context_menu(&obj, x, y);
}));

obj.add_controller(right_click);

// start refresh timer
let nano_to_milli = 1000000;
Expand All @@ -168,6 +232,9 @@ impl ObjectImpl for TreeMapWidget {
if let Some(child) = self.child.borrow_mut().take() {
child.unparent();
}
if let Some(popover_menu) = self.popover_menu.borrow_mut().take() {
popover_menu.unparent();
}
}
}

Expand All @@ -189,7 +256,7 @@ fn update_rects(
layout.set_text(&format!(
"{} ({})",
&node.name,
bytes_display::bytes_display(node.size)
utils::bytes_display(node.size)
));
let pango_w = pango::units_from_double(gui_node.rect.width() as f64);
layout.set_width(pango_w);
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// prevent a command line window on Windows
#![windows_subsystem = "windows"]
mod bytes_display;
mod config;
mod filetree;
mod gui;
mod mounts;
mod node_color;
mod squarify;
mod utils;

fn main() {
dbg!(mounts::get_mounts());
Expand Down
7 changes: 7 additions & 0 deletions src/bytes_display.rs → src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,10 @@ pub fn bytes_display(bytes: u64) -> String {
format!("{}B", bytes)
}
}

pub fn abbreviate_string(s: &str, max_chars: usize) -> String {
match s.char_indices().nth(max_chars) {
None => s.to_string(),
Some((idx, _)) => s[..idx - 3].to_string() + "...",
}
}

0 comments on commit de2a032

Please sign in to comment.