xenid/src/gui.rs
2025-12-05 11:19:20 +00:00

195 lines
5.5 KiB
Rust

use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use adw::{Clamp, ToolbarView, prelude::*};
use adw::{ActionRow, Application, ApplicationWindow, HeaderBar};
use gtk::{
Box, Button, CssProvider, Grid, GridLayout, Label, ListBox, Orientation, PasswordEntry,
SelectionMode,
};
use crate::pipe;
use glib::clone;
fn build_ui(
app: &Application,
ctg_pipe: async_channel::Receiver<crate::pipe::CardToGUI>,
gtc_pipe: async_channel::Sender<crate::pipe::GUIToCard>,
) {
let main_box = Box::builder().orientation(Orientation::Vertical).build();
let password_grid = Grid::new();
let password_mask: Vec<Button> = (0..5)
.map(|f| {
Button::builder()
.name(format!("button_{}", f))
.icon_name("")
.can_focus(false)
.can_target(false)
.hexpand(true)
.css_classes(&["cell"][..])
.build()
})
.collect();
for i in 0..5 {
password_grid.attach(&password_mask[i], i as i32, 0, 1, 1);
}
let password_field = PasswordEntry::builder()
.margin_bottom(12)
.width_request(5)
.width_chars(0)
.css_classes(&["invisible"][..])
.build();
password_grid.attach(&password_field, 0, 0, 5, 1);
password_field.connect_changed(move |v| {
let count = v.text().len();
for i in 0..5 {
password_mask[i].set_icon_name(if count > i {
"window-close-symbolic"
} else {
""
});
}
if count > 5 {
v.delete_text(5, -1);
}
});
let info_label = Label::builder()
.label("...Processing")
.margin_bottom(12)
.build();
let button = Button::builder().label("Next").sensitive(false).build();
main_box.append(&info_label);
main_box.append(&password_grid);
main_box.append(&button);
let headerbar = HeaderBar::new();
let toolbar_view = ToolbarView::new();
toolbar_view.add_top_bar(&headerbar);
let c = Clamp::builder()
.child(&main_box)
.margin_end(16)
.margin_start(16)
.margin_end(16)
.build();
toolbar_view.set_content(Some(&c));
let window = ApplicationWindow::builder()
.application(app)
.title("Log in with DigiD")
.default_width(350)
// add content to window
.content(&toolbar_view)
.build();
password_field.connect_activate(clone!(
#[weak]
button,
move |_| {
button.emit_clicked();
}
));
button.connect_clicked(clone!(
#[weak]
password_field,
move |btn| {
btn.set_sensitive(false);
let pass = password_field.text().to_string();
gtc_pipe.send_blocking(pipe::GUIToCard::PIN(pass)).unwrap();
}
));
gdk::glib::spawn_future_local(clone!(
#[weak]
info_label,
#[weak]
button,
#[weak]
window,
#[weak]
app,
#[weak]
password_field,
async move {
while let Ok(msg) = ctg_pipe.recv().await {
match msg {
pipe::CardToGUI::AuthenticationTarget { target } => {
info_label.set_text(&format!("Enter your PIN to log in to {}", target));
}
pipe::CardToGUI::WaitForCard => {
button.set_sensitive(false);
button.set_label("Place your card on the reader.");
}
pipe::CardToGUI::ReadyForPIN { message } => {
let no_special = message.is_none();
button.set_label(&message.unwrap_or_else(|| String::from("Next")));
button.set_sensitive(true);
if no_special && password_field.text().len() == 5 {
button.emit_clicked();
}
}
pipe::CardToGUI::ProcessingStep { step: _ } => {}
pipe::CardToGUI::ProcessingMessage { message } => {
button.set_label(&message);
}
pipe::CardToGUI::Done => {
window.close();
app.quit();
}
}
}
}
));
window.connect_has_focus_notify(move |f| {
if f.has_focus() {
password_field.grab_focus();
}
});
window.present();
}
pub fn run_gui(
ctg_pipe_r: async_channel::Receiver<crate::pipe::CardToGUI>,
gtc_pipe_s: async_channel::Sender<crate::pipe::GUIToCard>,
) {
let application = Application::builder()
.application_id("moe.puck.XeniD")
.build();
let ctg_pipe_r = RefCell::new(Some(ctg_pipe_r));
let gtc_pipe_s = RefCell::new(Some(gtc_pipe_s));
application.connect_activate(move |app| {
let provider = CssProvider::new();
provider
.load_from_string(".cell { margin: 6px; padding: 18px; } .invisible { opacity: 0; }");
gtk::style_context_add_provider_for_display(
&gdk::Display::default().expect("Could not connect to a display."),
&provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
build_ui(app, ctg_pipe_r.take().unwrap(), gtc_pipe_s.take().unwrap());
});
application.run_with_args::<glib::GString>(&[]);
}