implement shared features as traits

This commit is contained in:
yggverse 2025-02-15 03:09:26 +02:00
parent 6d49c79a0d
commit 725e764701
3 changed files with 118 additions and 98 deletions

View file

@ -1,5 +1,13 @@
const DEFAULT: &str = "-"; mod display;
const DATE_TIME_FORMAT: &str = "%Y.%m.%d %H:%M:%S"; mod format;
use display::Display;
use format::Format;
use gtk::{
gio::FileInfo, pango::EllipsizeMode, Align, ColumnViewColumn, Label, ListItem,
SignalListItemFactory,
};
pub trait Column { pub trait Column {
fn icon() -> Self; fn icon() -> Self;
@ -11,10 +19,10 @@ pub trait Column {
fn access_date_time(width: i32) -> Self; fn access_date_time(width: i32) -> Self;
} }
impl Column for gtk::ColumnViewColumn { impl Column for ColumnViewColumn {
fn icon() -> Self { fn icon() -> Self {
use gtk::{ use gtk::{
gio::{FileInfo, FileType}, gio::FileInfo,
prelude::{BoxExt, Cast, ListItemExt, WidgetExt}, prelude::{BoxExt, Cast, ListItemExt, WidgetExt},
Align, ColumnViewColumn, ListItem, SignalListItemFactory, Align, ColumnViewColumn, ListItem, SignalListItemFactory,
}; };
@ -34,24 +42,15 @@ impl Column for gtk::ColumnViewColumn {
.symbolic_icon() .symbolic_icon()
.unwrap(), .unwrap(),
); );
image.set_tooltip_text( image.set_tooltip_text(Some(
match list_item list_item
.item() .item()
.unwrap() .unwrap()
.downcast_ref::<FileInfo>() .downcast_ref::<FileInfo>()
.unwrap() .unwrap()
.file_type() .file_type()
{ .as_str(),
FileType::Unknown => Some("Unknown"), ));
FileType::Regular => Some("File"),
FileType::Directory => Some("Directory"),
FileType::SymbolicLink => Some("SymbolicLink"),
FileType::Special => Some("Special"),
FileType::Shortcut => Some("Shortcut"),
FileType::Mountable => Some("Mountable"),
_ => None,
},
);
let container = gtk::Box::builder().halign(Align::Center).build(); // prevents `gtk::Image` blur let container = gtk::Box::builder().halign(Align::Center).build(); // prevents `gtk::Image` blur
container.append(&image); container.append(&image);
list_item.set_child(Some(&container)); list_item.set_child(Some(&container));
@ -62,21 +61,21 @@ impl Column for gtk::ColumnViewColumn {
} }
fn name(width: i32) -> Self { fn name(width: i32) -> Self {
gtk::ColumnViewColumn::builder() ColumnViewColumn::builder()
.fixed_width(width) .fixed_width(width)
.resizable(true) .resizable(true)
.title("Name") .title("Name")
.factory(&{ .factory(&{
let factory = gtk::SignalListItemFactory::new(); let factory = SignalListItemFactory::new();
factory.connect_bind(|_, this| { factory.connect_bind(|_, this| {
use gtk::prelude::{Cast, /*FileExt,*/ ListItemExt}; use gtk::prelude::{Cast, /*FileExt,*/ ListItemExt};
let list_item = this.downcast_ref::<gtk::ListItem>().unwrap(); let list_item = this.downcast_ref::<ListItem>().unwrap();
let item = list_item.item().unwrap(); let item = list_item.item().unwrap();
let file_info = item.downcast_ref::<gtk::gio::FileInfo>().unwrap(); let file_info = item.downcast_ref::<FileInfo>().unwrap();
list_item.set_child(Some( list_item.set_child(Some(
&gtk::Label::builder() &Label::builder()
.halign(gtk::Align::Start) .halign(Align::Start)
.ellipsize(gtk::pango::EllipsizeMode::Middle) .ellipsize(EllipsizeMode::Middle)
.label(file_info.display_name()) .label(file_info.display_name())
/*.tooltip_text( /*.tooltip_text(
file_info file_info
@ -98,22 +97,22 @@ impl Column for gtk::ColumnViewColumn {
} }
fn size(width: i32) -> Self { fn size(width: i32) -> Self {
gtk::ColumnViewColumn::builder() ColumnViewColumn::builder()
.fixed_width(width) .fixed_width(width)
.resizable(true) .resizable(true)
.title("Size") .title("Size")
.factory(&{ .factory(&{
let factory = gtk::SignalListItemFactory::new(); let factory = SignalListItemFactory::new();
factory.connect_bind(|_, this| { factory.connect_bind(|_, this| {
use crate::tool::Format; use crate::tool::Format;
use gtk::prelude::{Cast, ListItemExt}; use gtk::prelude::{Cast, ListItemExt};
let list_item = this.downcast_ref::<gtk::ListItem>().unwrap(); let list_item = this.downcast_ref::<ListItem>().unwrap();
let item = list_item.item().unwrap(); let item = list_item.item().unwrap();
let file_info = item.downcast_ref::<gtk::gio::FileInfo>().unwrap(); let file_info = item.downcast_ref::<FileInfo>().unwrap();
list_item.set_child(Some( list_item.set_child(Some(
&gtk::Label::builder() &Label::builder()
.halign(gtk::Align::Start) .halign(Align::Start)
.ellipsize(gtk::pango::EllipsizeMode::Middle) .ellipsize(EllipsizeMode::Middle)
.label((file_info.size() as usize).bytes()) .label((file_info.size() as usize).bytes())
.build(), .build(),
)); ));
@ -124,39 +123,22 @@ impl Column for gtk::ColumnViewColumn {
} }
fn content_type(width: i32) -> Self { fn content_type(width: i32) -> Self {
gtk::ColumnViewColumn::builder() ColumnViewColumn::builder()
.fixed_width(width) .fixed_width(width)
.resizable(true) .resizable(true)
.title("Content Type") .title("Content Type")
.factory(&{ .factory(&{
let factory = gtk::SignalListItemFactory::new(); let factory = SignalListItemFactory::new();
factory.connect_bind(|_, this| { factory.connect_bind(|_, this| {
use gtk::prelude::{Cast, ListItemExt}; use gtk::prelude::{Cast, ListItemExt};
let list_item = this.downcast_ref::<gtk::ListItem>().unwrap(); let list_item = this.downcast_ref::<ListItem>().unwrap();
let item = list_item.item().unwrap(); let item = list_item.item().unwrap();
let file_info = item.downcast_ref::<gtk::gio::FileInfo>().unwrap(); let file_info = item.downcast_ref::<FileInfo>().unwrap();
let content_type: gtk::glib::GString = match file_info.content_type() {
Some(content_type) => {
let display_name = file_info.display_name();
if content_type == "text/plain" {
if display_name.ends_with(".gmi")
|| display_name.ends_with(".gemini")
{
"text/gemini".into()
} else {
content_type
}
} else {
content_type
}
}
None => DEFAULT.into(),
};
list_item.set_child(Some( list_item.set_child(Some(
&gtk::Label::builder() &Label::builder()
.halign(gtk::Align::Start) .halign(Align::Start)
.ellipsize(gtk::pango::EllipsizeMode::Middle) .ellipsize(EllipsizeMode::Middle)
.label(content_type) .label(file_info.format_content_type())
.build(), .build(),
)); ));
}); });
@ -166,28 +148,22 @@ impl Column for gtk::ColumnViewColumn {
} }
fn creation_date_time(width: i32) -> Self { fn creation_date_time(width: i32) -> Self {
gtk::ColumnViewColumn::builder() ColumnViewColumn::builder()
.fixed_width(width) .fixed_width(width)
.resizable(true) .resizable(true)
.title("Created") .title("Created")
.factory(&{ .factory(&{
let factory = gtk::SignalListItemFactory::new(); let factory = SignalListItemFactory::new();
factory.connect_bind(|_, this| { factory.connect_bind(|_, this| {
use gtk::prelude::{Cast, ListItemExt}; use gtk::prelude::{Cast, ListItemExt};
let list_item = this.downcast_ref::<gtk::ListItem>().unwrap(); let list_item = this.downcast_ref::<ListItem>().unwrap();
let item = list_item.item().unwrap(); let item = list_item.item().unwrap();
let file_info = item.downcast_ref::<gtk::gio::FileInfo>().unwrap(); let file_info = item.downcast_ref::<FileInfo>().unwrap();
list_item.set_child(Some( list_item.set_child(Some(
&gtk::Label::builder() &Label::builder()
.halign(gtk::Align::Start) .halign(Align::Start)
.ellipsize(gtk::pango::EllipsizeMode::Middle) .ellipsize(EllipsizeMode::Middle)
.label( .label(file_info.format_date_time())
file_info
.creation_date_time()
.unwrap()
.format(DATE_TIME_FORMAT) // @TODO optional
.unwrap_or(DEFAULT.into()),
)
.build(), .build(),
)); ));
}); });
@ -197,28 +173,22 @@ impl Column for gtk::ColumnViewColumn {
} }
fn modification_date_time(width: i32) -> Self { fn modification_date_time(width: i32) -> Self {
gtk::ColumnViewColumn::builder() ColumnViewColumn::builder()
.fixed_width(width) .fixed_width(width)
.resizable(true) .resizable(true)
.title("Modified") .title("Modified")
.factory(&{ .factory(&{
let factory = gtk::SignalListItemFactory::new(); let factory = SignalListItemFactory::new();
factory.connect_bind(|_, this| { factory.connect_bind(|_, this| {
use gtk::prelude::{Cast, ListItemExt}; use gtk::prelude::{Cast, ListItemExt};
let list_item = this.downcast_ref::<gtk::ListItem>().unwrap(); let list_item = this.downcast_ref::<ListItem>().unwrap();
let item = list_item.item().unwrap(); let item = list_item.item().unwrap();
let file_info = item.downcast_ref::<gtk::gio::FileInfo>().unwrap(); let file_info = item.downcast_ref::<FileInfo>().unwrap();
list_item.set_child(Some( list_item.set_child(Some(
&gtk::Label::builder() &Label::builder()
.halign(gtk::Align::Start) .halign(Align::Start)
.ellipsize(gtk::pango::EllipsizeMode::Middle) .ellipsize(EllipsizeMode::Middle)
.label( .label(file_info.format_date_time())
file_info
.modification_date_time()
.unwrap()
.format(DATE_TIME_FORMAT) // @TODO optional
.unwrap_or(DEFAULT.into()),
)
.build(), .build(),
)); ));
}); });
@ -228,28 +198,22 @@ impl Column for gtk::ColumnViewColumn {
} }
fn access_date_time(width: i32) -> Self { fn access_date_time(width: i32) -> Self {
gtk::ColumnViewColumn::builder() ColumnViewColumn::builder()
.fixed_width(width) .fixed_width(width)
.resizable(true) .resizable(true)
.title("Accessed") .title("Accessed")
.factory(&{ .factory(&{
let factory = gtk::SignalListItemFactory::new(); let factory = SignalListItemFactory::new();
factory.connect_bind(|_, this| { factory.connect_bind(|_, this| {
use gtk::prelude::{Cast, ListItemExt}; use gtk::prelude::{Cast, ListItemExt};
let list_item = this.downcast_ref::<gtk::ListItem>().unwrap(); let list_item = this.downcast_ref::<ListItem>().unwrap();
let item = list_item.item().unwrap(); let item = list_item.item().unwrap();
let file_info = item.downcast_ref::<gtk::gio::FileInfo>().unwrap(); let file_info = item.downcast_ref::<FileInfo>().unwrap();
list_item.set_child(Some( list_item.set_child(Some(
&gtk::Label::builder() &Label::builder()
.halign(gtk::Align::Start) .halign(Align::Start)
.ellipsize(gtk::pango::EllipsizeMode::Middle) .ellipsize(EllipsizeMode::Middle)
.label( .label(file_info.format_date_time())
file_info
.access_date_time()
.unwrap()
.format(DATE_TIME_FORMAT) // @TODO optional
.unwrap_or(DEFAULT.into()),
)
.build(), .build(),
)); ));
}); });

View file

@ -0,0 +1,20 @@
use gtk::gio::FileType;
pub trait Display {
fn as_str(&self) -> &str;
}
impl Display for FileType {
fn as_str(&self) -> &str {
match self {
FileType::Unknown => "Unknown",
FileType::Regular => "File",
FileType::Directory => "Directory",
FileType::SymbolicLink => "SymbolicLink",
FileType::Special => "Special",
FileType::Shortcut => "Shortcut",
FileType::Mountable => "Mountable",
_ => "Undefined",
}
}
}

View file

@ -0,0 +1,36 @@
use gtk::{gio::FileInfo, glib::GString};
const DEFAULT: &str = "-";
pub trait Format {
fn format_content_type(&self) -> GString;
fn format_date_time(&self) -> GString;
}
impl Format for FileInfo {
fn format_content_type(&self) -> GString {
match self.content_type() {
Some(content_type) => {
let display_name = self.display_name();
if content_type == "text/plain" {
if display_name.ends_with(".gmi") || display_name.ends_with(".gemini") {
"text/gemini".into()
} else {
content_type
}
} else {
content_type
}
}
None => DEFAULT.into(),
}
}
fn format_date_time(&self) -> GString {
match self.creation_date_time() {
Some(date_time) => date_time
.format("%Y.%m.%d %H:%M:%S") // @TODO optional
.unwrap_or(DEFAULT.into()),
None => DEFAULT.into(),
}
}
}