Skip to content

Commit 78e9cb8

Browse files
committed
feat(icons): add new symbolic SVG icons for various actions
- Added lucide-package-search-symbolic.svg - Added lucide-paint-bucket-symbolic.svg - Added lucide-radio-tower-symbolic.svg - Added lucide-server-cog-symbolic.svg - Added lucide-server-symbolic.svg - Added lucide-timer-reset-symbolic.svg - Added lucide-toy-brick-symbolic.svg - Added lucide-user-symbolic.svg - Added lucide-warehouse-symbolic.svg - Added lucide-waypoints-symbolic.svg
1 parent 4f476c6 commit 78e9cb8

43 files changed

Lines changed: 548 additions & 29 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release-windows.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ jobs:
110110
cp -r /clang64/share/themes "$bundle/share/" || true
111111
mkdir -p "$bundle/share/aetheris"
112112
cp data/style.css "$bundle/share/aetheris/style.css"
113+
mkdir -p "$bundle/share/icons/hicolor/scalable/actions"
114+
cp data/icons/hicolor/scalable/actions/*.svg "$bundle/share/icons/hicolor/scalable/actions/"
113115
mkdir -p "$bundle/share/locale/pt_BR/LC_MESSAGES"
114116
cp po/pt_BR/LC_MESSAGES/org.luminusos.Aetheris.mo "$bundle/share/locale/pt_BR/LC_MESSAGES/org.luminusos.Aetheris.mo"
115117
cp -r /clang64/lib/gdk-pixbuf-2.0 "$bundle/lib/" || true

build-aux/org.luminusos.Aetheris.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464
"install -Dm644 data/icons/hicolor/256x256/apps/org.luminusos.Aetheris.png /app/share/icons/hicolor/256x256/apps/org.luminusos.Aetheris.png",
6565
"install -Dm644 data/icons/hicolor/512x512/apps/org.luminusos.Aetheris.png /app/share/icons/hicolor/512x512/apps/org.luminusos.Aetheris.png",
6666
"install -Dm644 data/icons/hicolor/symbolic/apps/org.luminusos.Aetheris-symbolic.svg /app/share/icons/hicolor/symbolic/apps/org.luminusos.Aetheris-symbolic.svg",
67-
"install -Dm644 data/icons/hicolor/scalable/actions/nautilus-search-filters-symbolic.svg /app/share/icons/hicolor/scalable/actions/nautilus-search-filters-symbolic.svg"
67+
"mkdir -p /app/share/icons/hicolor/scalable/actions",
68+
"cp data/icons/hicolor/scalable/actions/*.svg /app/share/icons/hicolor/scalable/actions/"
6869
],
6970
"sources": [{ "type": "dir", "path": ".." }]
7071
}

crates/aetheris-app/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ assets = [
2121
"../../data/icons/hicolor/256x256/apps/org.luminusos.Aetheris.png",
2222
"../../data/icons/hicolor/512x512/apps/org.luminusos.Aetheris.png",
2323
"../../data/icons/hicolor/symbolic/apps/org.luminusos.Aetheris-symbolic.svg",
24+
"../../data/icons/hicolor/scalable/actions",
2425
"../../po/pt_BR/LC_MESSAGES/org.luminusos.Aetheris.mo",
2526
]
2627
auto_link = true

crates/aetheris-app/src/app.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ mod yaml;
3838

3939
use i18n::{tr, tr_format, trn};
4040
use projects::{
41-
DetailTarget, ObjectColumn, ObjectTableColumn, PodLogTarget, Project, ProjectStore,
42-
ResourceSection, StatusFilter,
41+
DetailTarget, ObjectColumn, ObjectFavorite, ObjectTableColumn, PodLogTarget, Project,
42+
ProjectStore, ResourceSection, StatusFilter,
4343
};
4444
use widgets::{
4545
is_access_resource, is_cluster_resource, is_configuration_resource, is_network_resource,
@@ -84,6 +84,7 @@ pub(super) struct DetailPane {
8484
apply_button: gtk::Button,
8585
download_yaml_button: gtk::Button,
8686
delete_button: gtk::Button,
87+
favorite_button: gtk::Button,
8788
terminal_button: gtk::Button,
8889
yaml_buffer: sourceview5::Buffer,
8990
events_list: gtk::ListBox,
@@ -169,6 +170,7 @@ pub struct App {
169170
status_label: gtk::Label,
170171
spinner: gtk::Spinner,
171172
resource_list: gtk::ListBox,
173+
favorite_object_list: gtk::ListBox,
172174
/// Backing model for `object_view`. The `ColumnView` only realizes
173175
/// widgets for on-screen rows, so this can hold tens of thousands of
174176
/// objects without the widget tree growing with it.
@@ -256,7 +258,9 @@ pub enum AppMsg {
256258
ObjectColumnToggled(u32),
257259
ObjectColumnResized(ObjectTableColumn, i32),
258260
EditCurrentCluster,
259-
ResourceChanged(usize),
261+
ResourceChanged(usize, ResourceSection),
262+
ToggleCurrentObjectFavorite,
263+
FavoriteObjectActivated(ObjectFavorite),
260264
SearchChanged(String),
261265
ObjectActivated(i32),
262266
RelatedPodActivated(i32),

crates/aetheris-app/src/app/component.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ impl Component for App {
228228
let resource_list = gtk::ListBox::new();
229229
resource_list.add_css_class("boxed-list");
230230
resource_list.set_selection_mode(gtk::SelectionMode::None);
231+
let favorite_object_list = gtk::ListBox::new();
232+
favorite_object_list.add_css_class("boxed-list");
233+
favorite_object_list.set_selection_mode(gtk::SelectionMode::None);
231234
let object_store = gtk::gio::ListStore::new::<gtk::glib::BoxedAnyObject>();
232235
let object_view = gtk::ColumnView::builder()
233236
.single_click_activate(true)
@@ -333,6 +336,18 @@ impl Component for App {
333336
detail_delete_button.add_css_class("flat");
334337
detail_delete_button.set_size_request(34, 34);
335338
detail_delete_button.set_valign(gtk::Align::Center);
339+
let detail_favorite_button = gtk::Button::builder()
340+
.icon_name(super::widgets::available_icon_name(
341+
"aetheris-object-favorite-outline-symbolic",
342+
"non-starred-symbolic",
343+
))
344+
.tooltip_text(tr("Add to favorites"))
345+
.sensitive(false)
346+
.visible(false)
347+
.build();
348+
detail_favorite_button.add_css_class("flat");
349+
detail_favorite_button.set_size_request(34, 34);
350+
detail_favorite_button.set_valign(gtk::Align::Center);
336351
let detail_terminal_button = gtk::Button::builder()
337352
.icon_name("utilities-terminal-symbolic")
338353
.tooltip_text(tr("Open terminal"))
@@ -606,11 +621,13 @@ impl Component for App {
606621
cluster_menu_button: &cluster_menu_button,
607622
namespace_menu_button: &namespace_menu_button,
608623
resource_list: &resource_list,
624+
favorite_object_list: &favorite_object_list,
609625
});
610626
let content = build_content(ContentWidgets {
611627
sidebar_toggle_button: &sidebar_toggle_button,
612628
detail_back_button: &detail_back_button,
613629
delete_button: &detail_delete_button,
630+
favorite_button: &detail_favorite_button,
614631
create_yaml_button: &create_yaml_button,
615632
refresh_button: &refresh_button,
616633
terminal_button: &detail_terminal_button,
@@ -813,6 +830,10 @@ impl Component for App {
813830
let sender = sender.clone();
814831
move |_| sender.input(AppMsg::DeleteObject)
815832
});
833+
detail_favorite_button.connect_clicked({
834+
let sender = sender.clone();
835+
move |_| sender.input(AppMsg::ToggleCurrentObjectFavorite)
836+
});
816837
detail_terminal_button.connect_clicked({
817838
let sender = sender.clone();
818839
move |_| sender.input(AppMsg::ShowPodTerminal)
@@ -916,6 +937,7 @@ impl Component for App {
916937
status_label,
917938
spinner,
918939
resource_list,
940+
favorite_object_list,
919941
object_store,
920942
object_sorted,
921943
object_columns,
@@ -940,6 +962,7 @@ impl Component for App {
940962
apply_button: detail_apply_button,
941963
download_yaml_button: detail_download_yaml_button,
942964
delete_button: detail_delete_button,
965+
favorite_button: detail_favorite_button,
943966
terminal_button: detail_terminal_button,
944967
yaml_buffer: detail_yaml_buffer,
945968
events_list: detail_events_list,

crates/aetheris-app/src/app/handler.rs

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -491,22 +491,40 @@ impl App {
491491
self.schedule_project_save(&sender);
492492
}
493493
}
494-
AppMsg::ResourceChanged(index) => {
495-
if self.resources.get(index).is_some() {
496-
let next = Some(index);
497-
if self.selected_resource != next {
498-
self.selected_resource = next;
499-
self.selected_resource_section =
500-
ResourceSection::for_resource(&self.resources[index]);
501-
self.rebuild_resource_list(Some(sender.clone()));
502-
self.present_content_panel();
503-
self.show_object_list();
504-
self.sync_object_columns();
505-
self.stop_log_stream();
506-
self.stop_port_forward();
507-
self.refresh_objects(sender);
508-
}
494+
AppMsg::ResourceChanged(index, _section) => {
495+
let Some(resource) = self.resources.get(index) else {
496+
return;
497+
};
498+
if self.selected_resource == Some(index) {
499+
return;
509500
}
501+
self.selected_resource = Some(index);
502+
self.selected_resource_section = ResourceSection::for_resource(resource);
503+
self.rebuild_resource_list(Some(sender.clone()));
504+
self.present_content_panel();
505+
self.show_object_list();
506+
self.sync_object_columns();
507+
self.stop_log_stream();
508+
self.stop_port_forward();
509+
self.refresh_objects(sender);
510+
}
511+
AppMsg::ToggleCurrentObjectFavorite => {
512+
let Some(target) = self.detail.target.clone() else {
513+
return;
514+
};
515+
self.projects.toggle_object_favorite(&target);
516+
self.save_projects_or_toast();
517+
self.sync_detail_favorite_button();
518+
self.rebuild_favorite_object_list(Some(sender.clone()));
519+
}
520+
AppMsg::FavoriteObjectActivated(favorite) => {
521+
self.open_object_detail(
522+
favorite.context.clone(),
523+
favorite.resource(),
524+
favorite.namespace(),
525+
favorite.name.clone(),
526+
sender,
527+
);
510528
}
511529
AppMsg::SearchChanged(query) => {
512530
self.search_query = query;
@@ -556,13 +574,19 @@ impl App {
556574
return;
557575
}
558576
self.loading = false;
559-
self.detail.target = None;
560577
self.detail.log_target = None;
561578
self.detail.exec_target = None;
562579
self.detail.port_forward_target = None;
563580
self.sync_log_controls();
564-
self.sync_terminal_controls();
565-
self.sync_port_forward_controls();
581+
// Keep `target` set (rather than clearing it) and still open
582+
// the detail page: the object is gone, but this is the only
583+
// place with a favorite/star button, so a stale favorite
584+
// must be reachable here to unfavorite it.
585+
if let Some(target) = self.detail.target.clone() {
586+
let placeholder = unavailable_object_detail(&target);
587+
self.populate_detail_dialog(&placeholder);
588+
self.show_detail_page(&placeholder.name);
589+
}
566590
self.status = tr("Unable to load object detail.");
567591
self.sync_status();
568592
self.toaster.add_toast(adw::Toast::new(&error));
@@ -617,6 +641,10 @@ impl App {
617641
self.show_object_list();
618642
self.stop_log_stream();
619643
self.stop_port_forward();
644+
self.detail.target = None;
645+
self.detail.log_target = None;
646+
self.detail.exec_target = None;
647+
self.detail.port_forward_target = None;
620648
self.sync_log_controls();
621649
self.sync_terminal_controls();
622650
self.sync_port_forward_controls();

crates/aetheris-app/src/app/layout.rs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub(super) struct SidebarWidgets<'a> {
77
pub(super) cluster_menu_button: &'a gtk::MenuButton,
88
pub(super) namespace_menu_button: &'a gtk::MenuButton,
99
pub(super) resource_list: &'a gtk::ListBox,
10+
pub(super) favorite_object_list: &'a gtk::ListBox,
1011
}
1112

1213
pub(super) fn build_sidebar(widgets: SidebarWidgets<'_>) -> adw::NavigationPage {
@@ -29,10 +30,69 @@ pub(super) fn build_sidebar(widgets: SidebarWidgets<'_>) -> adw::NavigationPage
2930

3031
container.append(&namespace_group);
3132

32-
let resources_group = gtk::Box::new(gtk::Orientation::Vertical, 8);
33-
resources_group.append(&section_title(&tr("Resources")));
34-
resources_group.append(widgets.resource_list);
35-
container.append(&resources_group);
33+
let resources_page = gtk::Box::new(gtk::Orientation::Vertical, 8);
34+
resources_page.append(widgets.resource_list);
35+
36+
let favorites_page = gtk::Box::new(gtk::Orientation::Vertical, 8);
37+
favorites_page.append(widgets.favorite_object_list);
38+
39+
let sidebar_stack = gtk::Stack::builder()
40+
.hhomogeneous(false)
41+
.vhomogeneous(false)
42+
.build();
43+
sidebar_stack.add_named(&resources_page, Some("resources"));
44+
sidebar_stack.add_named(&favorites_page, Some("favorites"));
45+
46+
let toggle_box = gtk::Box::new(gtk::Orientation::Horizontal, 4);
47+
let resources_toggle = gtk::ToggleButton::builder()
48+
.icon_name("view-list-symbolic")
49+
.tooltip_text(tr("Resources"))
50+
.hexpand(true)
51+
.active(true)
52+
.css_classes(["flat"])
53+
.build();
54+
let favorites_toggle = gtk::ToggleButton::builder()
55+
.icon_name("aetheris-object-favorite-symbolic")
56+
.tooltip_text(tr("Favorites"))
57+
.hexpand(true)
58+
.css_classes(["flat"])
59+
.build();
60+
toggle_box.append(&resources_toggle);
61+
toggle_box.append(&favorites_toggle);
62+
63+
resources_toggle.connect_clicked({
64+
let resources_toggle = resources_toggle.clone();
65+
move |_| resources_toggle.set_active(true)
66+
});
67+
resources_toggle.connect_toggled({
68+
let favorites_toggle = favorites_toggle.clone();
69+
let sidebar_stack = sidebar_stack.clone();
70+
move |button| {
71+
if button.is_active() {
72+
favorites_toggle.set_active(false);
73+
sidebar_stack.set_visible_child_name("resources");
74+
}
75+
}
76+
});
77+
favorites_toggle.connect_clicked({
78+
let favorites_toggle = favorites_toggle.clone();
79+
move |_| favorites_toggle.set_active(true)
80+
});
81+
favorites_toggle.connect_toggled({
82+
let resources_toggle = resources_toggle.clone();
83+
let sidebar_stack = sidebar_stack.clone();
84+
move |button| {
85+
if button.is_active() {
86+
resources_toggle.set_active(false);
87+
sidebar_stack.set_visible_child_name("favorites");
88+
}
89+
}
90+
});
91+
92+
let objects_group = gtk::Box::new(gtk::Orientation::Vertical, 8);
93+
objects_group.append(&toggle_box);
94+
objects_group.append(&sidebar_stack);
95+
container.append(&objects_group);
3696

3797
let scrolled = gtk::ScrolledWindow::builder()
3898
.vexpand(true)
@@ -50,6 +110,7 @@ pub(super) struct ContentWidgets<'a> {
50110
pub(super) sidebar_toggle_button: &'a gtk::ToggleButton,
51111
pub(super) detail_back_button: &'a gtk::Button,
52112
pub(super) delete_button: &'a gtk::Button,
113+
pub(super) favorite_button: &'a gtk::Button,
53114
pub(super) create_yaml_button: &'a gtk::Button,
54115
pub(super) refresh_button: &'a gtk::Button,
55116
pub(super) terminal_button: &'a gtk::Button,
@@ -101,6 +162,7 @@ pub(super) fn build_content(widgets: ContentWidgets<'_>) -> adw::NavigationPage
101162
detail_actions.set_valign(gtk::Align::Center);
102163
detail_actions.set_halign(gtk::Align::End);
103164
detail_actions.append(widgets.terminal_button);
165+
detail_actions.append(widgets.favorite_button);
104166
detail_actions.append(widgets.delete_button);
105167
header.pack_end(&detail_actions);
106168
toolbar.add_top_bar(&header);

0 commit comments

Comments
 (0)