dev #1

Merged
Breadway merged 9 commits from dev into main 2026-07-10 23:15:12 +08:00
5 changed files with 98 additions and 9 deletions
Showing only changes of commit 8471cc02fe - Show all commits

2
Cargo.lock generated
View file

@ -119,7 +119,7 @@ dependencies = [
[[package]] [[package]]
name = "breadbar" name = "breadbar"
version = "0.2.2" version = "0.2.3"
dependencies = [ dependencies = [
"bread-theme", "bread-theme",
"futures-lite", "futures-lite",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "breadbar" name = "breadbar"
version = "0.2.2" version = "0.2.3"
edition = "2021" edition = "2021"
description = "Minimal status bar and notification daemon for Hyprland on Wayland" description = "Minimal status bar and notification daemon for Hyprland on Wayland"
license = "MIT" license = "MIT"

View file

@ -142,3 +142,19 @@ pub fn spawn_join(ssid: String) {
}); });
} }
/// Fire-and-forget: save a new network with its password, then join it.
pub fn spawn_add_and_join(ssid: String, password: String) {
relm4::spawn(async move {
let added = tokio::process::Command::new("breadcrumbs")
.args(["add", &ssid, &password])
.output()
.await;
if matches!(added, Ok(o) if o.status.success()) {
let _ = tokio::process::Command::new("breadcrumbs")
.args(["join", &ssid])
.output()
.await;
}
});
}

View file

@ -814,7 +814,6 @@ impl App {
row.add_css_class("wifi-popover-row"); row.add_css_class("wifi-popover-row");
if !entry.saved { if !entry.saved {
row.add_css_class("wifi-popover-row-unsaved"); row.add_css_class("wifi-popover-row-unsaved");
row.set_sensitive(false);
} }
let is_current = entry.ssid == self.current_ssid; let is_current = entry.ssid == self.current_ssid;
if is_current { if is_current {
@ -837,13 +836,16 @@ impl App {
row_box.append(&lbl); row_box.append(&lbl);
row.set_child(Some(&row_box)); row.set_child(Some(&row_box));
if entry.saved {
let ssid_clone = entry.ssid.clone(); let ssid_clone = entry.ssid.clone();
let saved = entry.saved;
row.connect_clicked(move |btn| { row.connect_clicked(move |btn| {
if saved {
bar::wifi::spawn_join(ssid_clone.clone()); bar::wifi::spawn_join(ssid_clone.clone());
} else {
show_add_network_dialog(btn, ssid_clone.clone());
}
close_parent_popover(btn); close_parent_popover(btn);
}); });
}
self.wifi_popover_box.append(&row); self.wifi_popover_box.append(&row);
} }
} }
@ -901,6 +903,76 @@ fn close_parent_popover(widget: &gtk4::Button) {
} }
} }
/// Small modal prompting for a password, then saves + joins the network via
/// `breadcrumbs add` + `breadcrumbs join`.
fn show_add_network_dialog(anchor: &gtk4::Button, ssid: String) {
let dialog = gtk4::Window::new();
dialog.set_title(Some(&format!("Add “{ssid}")));
dialog.set_resizable(false);
dialog.add_css_class("wifi-add-dialog");
if let Some(root) = anchor.root() {
if let Ok(win) = root.downcast::<gtk4::Window>() {
dialog.set_transient_for(Some(&win));
dialog.set_modal(true);
}
}
let body = gtk4::Box::new(gtk4::Orientation::Vertical, 8);
body.set_margin_top(12);
body.set_margin_bottom(12);
body.set_margin_start(12);
body.set_margin_end(12);
let lbl = gtk4::Label::new(Some(&format!("Password for {ssid}")));
lbl.set_xalign(0.0);
body.append(&lbl);
let entry = gtk4::PasswordEntry::new();
entry.set_show_peek_icon(true);
body.append(&entry);
let btn_row = gtk4::Box::new(gtk4::Orientation::Horizontal, 8);
btn_row.set_halign(gtk4::Align::End);
let cancel_btn = gtk4::Button::with_label("Cancel");
let connect_btn = gtk4::Button::with_label("Connect");
connect_btn.add_css_class("suggested-action");
btn_row.append(&cancel_btn);
btn_row.append(&connect_btn);
body.append(&btn_row);
dialog.set_child(Some(&body));
let d = dialog.clone();
cancel_btn.connect_clicked(move |_| d.close());
let dialog_for_connect = dialog.clone();
let entry_for_connect = entry.clone();
let ssid_for_connect = ssid.clone();
connect_btn.connect_clicked(move |_| {
let password = entry_for_connect.text().to_string();
if password.is_empty() {
return;
}
bar::wifi::spawn_add_and_join(ssid_for_connect.clone(), password);
dialog_for_connect.close();
});
let dialog_for_activate = dialog.clone();
let entry_for_activate = entry.clone();
let ssid_for_activate = ssid.clone();
entry.connect_activate(move |_| {
let password = entry_for_activate.text().to_string();
if password.is_empty() {
return;
}
bar::wifi::spawn_add_and_join(ssid_for_activate.clone(), password);
dialog_for_activate.close();
});
dialog.present();
entry.grab_focus();
}
fn stat_pair(icon_svg: &str, label: &gtk4::Label) -> gtk4::Box { fn stat_pair(icon_svg: &str, label: &gtk4::Label) -> gtk4::Box {
let pair = gtk4::Box::new(gtk4::Orientation::Horizontal, 0); let pair = gtk4::Box::new(gtk4::Orientation::Horizontal, 0);
pair.add_css_class("stat-pair"); pair.add_css_class("stat-pair");

View file

@ -50,6 +50,7 @@ fn load_css() -> String {
.wifi-popover-row-active {{ color: {accent}; }}\ .wifi-popover-row-active {{ color: {accent}; }}\
.wifi-popover-row-unsaved {{ opacity: 0.4; }}\ .wifi-popover-row-unsaved {{ opacity: 0.4; }}\
.wifi-popover-loading {{ opacity: 0.5; padding: 8px; }}\ .wifi-popover-loading {{ opacity: 0.5; padding: 8px; }}\
window.wifi-add-dialog {{ background-color: {bg_rgba}; color: {on_bg}; min-width: 240px; }}\
.media-widget {{ border-radius: 4px; padding: 0 6px; cursor: pointer; }}\ .media-widget {{ border-radius: 4px; padding: 0 6px; cursor: pointer; }}\
.media-widget:hover {{ background: alpha({on_bg}, 0.10); }}\ .media-widget:hover {{ background: alpha({on_bg}, 0.10); }}\
.media-indicator {{ font-size: 11px; opacity: 0.7; margin-right: 2px; }}\ .media-indicator {{ font-size: 11px; opacity: 0.7; margin-right: 2px; }}\