Skip to content

Commit 4e64768

Browse files
committed
Add first ui to add custom terminal
1 parent afc9b9c commit 4e64768

File tree

3 files changed

+364
-55
lines changed

3 files changed

+364
-55
lines changed

src/supported_terminals.rs

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ mod imp {
6262

6363
impl Default for TerminalRepository {
6464
fn default() -> Self {
65-
let custom_list_path = glib::user_data_dir().join("distroshelf-terminals.json");
65+
let custom_list_path = glib::user_data_dir().join("distroshelf-terminals.json");
6666
Self {
6767
list: RefCell::new(vec![]),
6868
custom_list_path,
@@ -79,30 +79,32 @@ mod imp {
7979
}
8080
}
8181

82-
8382
glib::wrapper! {
8483
pub struct TerminalRepository(ObjectSubclass<imp::TerminalRepository>);
8584
}
8685

8786
impl TerminalRepository {
8887
pub fn new() -> Self {
89-
let this: Self = glib::Object::builder()
90-
.build();
88+
let this: Self = glib::Object::builder().build();
9189

9290
let mut list = SUPPORTED_TERMINALS.clone();
9391
if let Ok(loaded_list) = Self::load_terminals_from_json(&this.imp().custom_list_path) {
9492
list.extend(loaded_list);
9593
} else {
96-
error!("Failed to load custom terminals from JSON file {:?}", &this.imp().custom_list_path);
94+
error!(
95+
"Failed to load custom terminals from JSON file {:?}",
96+
&this.imp().custom_list_path
97+
);
9798
}
98-
99+
99100
list.sort_by(|a, b| a.name.cmp(&b.name));
100101
this.imp().list.replace(list);
101102
this
102103
}
103104

104105
pub fn is_read_only(&self, name: &str) -> bool {
105-
self.imp().list
106+
self.imp()
107+
.list
106108
.borrow()
107109
.iter()
108110
.find(|x| x.name == name)
@@ -113,11 +115,13 @@ impl TerminalRepository {
113115
if self.is_read_only(terminal.name.as_str()) {
114116
return Err(anyhow::anyhow!("Cannot modify read-only terminal"));
115117
}
116-
let mut list = self.imp().list.borrow_mut();
117-
list.retain(|x| x.name != terminal.name);
118-
list.push(terminal);
118+
{
119+
let mut list = self.imp().list.borrow_mut();
120+
list.retain(|x| x.name != terminal.name);
121+
list.push(terminal);
119122

120-
list.sort_by(|a, b| a.name.cmp(&b.name));
123+
list.sort_by(|a, b| a.name.cmp(&b.name));
124+
}
121125

122126
self.save_terminals_to_json();
123127
Ok(())
@@ -127,16 +131,25 @@ impl TerminalRepository {
127131
if self.is_read_only(name) {
128132
return Err(anyhow::anyhow!("Cannot modify read-only terminal"));
129133
}
130-
self.imp().list.borrow_mut().retain(|x| x.name != name);
134+
{
135+
self.imp().list.borrow_mut().retain(|x| x.name != name);
136+
}
137+
self.save_terminals_to_json();
131138
Ok(())
132139
}
133140

134141
pub fn terminal_by_name(&self, name: &str) -> Option<Terminal> {
135-
self.imp().list.borrow().iter().find(|x| x.name == name).cloned()
142+
self.imp()
143+
.list
144+
.borrow()
145+
.iter()
146+
.find(|x| x.name == name)
147+
.cloned()
136148
}
137149

138150
pub fn terminal_by_program(&self, program: &str) -> Option<Terminal> {
139-
self.imp().list
151+
self.imp()
152+
.list
140153
.borrow()
141154
.iter()
142155
.find(|x| x.program == program)
@@ -148,7 +161,14 @@ impl TerminalRepository {
148161
}
149162

150163
fn save_terminals_to_json(&self) {
151-
let list: Vec<Terminal> = self.imp().list.borrow().iter().filter(|x| !x.read_only).cloned().collect::<Vec<_>>();
164+
let list: Vec<Terminal> = self
165+
.imp()
166+
.list
167+
.borrow()
168+
.iter()
169+
.filter(|x| !x.read_only)
170+
.cloned()
171+
.collect::<Vec<_>>();
152172
let json = serde_json::to_string(&*list).unwrap();
153173
std::fs::write(&self.imp().custom_list_path, json).unwrap();
154174
}
@@ -160,9 +180,16 @@ impl TerminalRepository {
160180
}
161181

162182
pub async fn default_terminal(&self) -> Option<Terminal> {
163-
let command = Command::new_with_args("flatpak-spawn", &["--host", "--", "get",
183+
let command = Command::new_with_args(
184+
"flatpak-spawn",
185+
&[
186+
"--host",
187+
"--",
188+
"get",
164189
"org.gnome.desktop.default-applications.terminal",
165-
"exec",]);
190+
"exec",
191+
],
192+
);
166193
let output = self.imp().command_runner.borrow().output(command.clone());
167194
let Ok(output) = output.await else {
168195
error!("Failed to get default terminal, running {:?}", &command);
@@ -173,11 +200,13 @@ impl TerminalRepository {
173200
return None;
174201
}
175202
info!("Default terminal program: {}", terminal_program);
176-
self.terminal_by_program(&terminal_program)
177-
.or_else(|| {
178-
error!("Terminal program {} not found in the list", terminal_program);
179-
None
180-
})
203+
self.terminal_by_program(&terminal_program).or_else(|| {
204+
error!(
205+
"Terminal program {} not found in the list",
206+
terminal_program
207+
);
208+
None
209+
})
181210
}
182211
}
183212

src/terminal_combo_row.rs

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
// This file is licensed under the same terms as the project it belongs to
33

44
use crate::root_store::RootStore;
5-
use crate::{supported_terminals};
5+
use crate::supported_terminals;
66
use adw::prelude::*;
77
use adw::subclass::prelude::*;
88
use glib::clone;
99
use glib::subclass::Signal;
1010
use glib::Properties;
11-
use gtk::glib;
11+
use gtk::{glib, StringObject};
1212
use std::cell::RefCell;
1313
use std::sync::OnceLock;
1414

1515
mod imp {
16+
use gtk::StringObject;
17+
1618
use super::*;
1719

1820
#[derive(Properties, Default)]
@@ -31,39 +33,24 @@ mod imp {
3133
obj.set_title("Preferred Terminal");
3234
obj.set_use_subtitle(true);
3335

34-
let terminals = self.root_store.borrow().clone().terminal_repository().all_terminals();
35-
let terminals = terminals
36-
.iter()
37-
.map(|x| x.name.as_ref())
38-
.collect::<Vec<_>>();
39-
40-
let selected_position = terminals.iter().position(|x| {
41-
Some(x)
42-
== obj
43-
.root_store()
44-
.selected_terminal()
45-
.as_ref()
46-
.map(|x| x.name.as_ref())
47-
.as_ref()
48-
});
49-
50-
let terminal_list = gtk::StringList::new(&terminals);
51-
obj.set_model(Some(&terminal_list));
52-
if let Some(selected_position) = selected_position {
53-
obj.set_selected(selected_position as u32);
54-
}
5536
obj.connect_selected_item_notify(clone!(
5637
#[weak]
5738
obj,
5839
move |combo| {
59-
let selected: gtk::StringObject = combo.selected_item().and_downcast().unwrap();
60-
if let Some(terminal) = obj.root_store().terminal_repository().terminal_by_name(&selected.string())
40+
let Some(selected) = combo.selected_item().and_downcast::<StringObject>() else {
41+
return;
42+
};
43+
if let Some(terminal) = obj
44+
.root_store()
45+
.terminal_repository()
46+
.terminal_by_name(&selected.string())
6147
{
62-
obj.root_store()
63-
.set_selected_terminal_name(&terminal.name);
48+
obj.root_store().set_selected_terminal_name(&terminal.name);
6449
}
6550
}
6651
));
52+
53+
obj.reload_terminals();
6754
}
6855

6956
fn signals() -> &'static [Signal] {
@@ -131,6 +118,39 @@ impl TerminalComboRow {
131118
.property("root-store", root_store)
132119
.build()
133120
}
121+
122+
pub fn set_selected_by_name(&self, name: &str) {
123+
let Some(terminals_strings) = self.model().unwrap().downcast::<gtk::StringList>().ok() else {
124+
return;
125+
};
126+
for i in 0..terminals_strings.n_items() {
127+
let Some(item) = terminals_strings.item(i).and_downcast::<StringObject>() else {
128+
continue;
129+
};
130+
if item.string() == name {
131+
self.set_selected(i);
132+
return;
133+
}
134+
}
135+
}
136+
pub fn reload_terminals(&self) {
137+
let terminals = self
138+
.root_store()
139+
.clone()
140+
.terminal_repository()
141+
.all_terminals();
142+
let terminals = terminals
143+
.iter()
144+
.map(|x| x.name.as_ref())
145+
.collect::<Vec<_>>();
146+
147+
let terminal_list = gtk::StringList::new(&terminals);
148+
self.set_model(Some(&terminal_list));
149+
150+
if let Some(selected_terminal) = self.root_store().selected_terminal() {
151+
self.set_selected_by_name(&selected_terminal.name);
152+
}
153+
}
134154
}
135155

136156
impl Default for TerminalComboRow {

0 commit comments

Comments
 (0)