fix(printers): page de test en auto-detection MIME, noms sanitaires compacts, actions en flex_row

La page de test est un template de banniere cups-filters (#PDF-BANNER), pas du PostScript: envoi en application/octet-stream pour laisser CUPS detecter le format. Le sanitiseur supprime crochets/parentheses et compacte les underscores (series [32C998] -> series_32C998). Les actions des details passent en flex_row pour eviter le debordement des boutons; bouton de confirmation du dialogue raccourci.
This commit is contained in:
Lionel DARNIS 2026-08-08 10:55:27 +02:00
parent 26dcaa6fd3
commit d5e57cc439
4 changed files with 31 additions and 15 deletions

View file

@ -113,7 +113,9 @@ pub fn print_test_page_sync(printer: &str) -> Result<(), String> {
let dest = cups_rs::get_destination(printer).map_err(|err| err.to_string())?;
let job = cups_rs::create_job(&dest, "COSMIC Settings test page")
.map_err(|err| err.to_string())?;
job.submit_file(TEST_PAGE, "application/postscript")
// Let CUPS sniff the format: the test page is a cups-filters PDF banner
// template, not PostScript.
job.submit_file(TEST_PAGE, "application/octet-stream")
.map_err(|err| err.to_string())
}
@ -177,21 +179,29 @@ pub async fn set_enabled(name: &str, enabled: bool) -> Result<(), String> {
/// Sanitize a display name into a valid CUPS queue name and make it unique
/// against the current printer list.
pub fn unique_name(raw: &str, existing: &[Printer]) -> String {
let sanitized: String = raw
.chars()
.map(|c| {
if c.is_alphanumeric() || matches!(c, '-' | '_' | '.') {
c
let mut sanitized = String::with_capacity(raw.len());
for c in raw.chars() {
if c.is_alphanumeric() {
sanitized.push(c);
} else if matches!(c, '[' | ']' | '(' | ')') {
// Brackets usually wrap a model identifier; drop them so
// "series [32C998]" becomes "series_32C998", not "series__32C998_".
continue;
} else {
'_'
sanitized.push('_');
}
}
})
.collect();
let base = if sanitized.is_empty() {
let base = sanitized
.split('_')
.filter(|part| !part.is_empty())
.collect::<Vec<_>>()
.join("_");
let base = if base.is_empty() {
"Printer".to_string()
} else {
sanitized
base
};
let mut candidate = base.clone();

View file

@ -112,7 +112,7 @@ impl page::Page<crate::pages::Message> for Page {
fn dialog(&self) -> Option<Element<'_, crate::pages::Message>> {
let Dialog::RemovePrinter { name } = self.dialog.as_ref()?;
let confirm = widget::button::destructive(fl!("printers-remove"))
let confirm = widget::button::destructive(fl!("printers-remove-confirm-button"))
.on_press(crate::pages::Message::from(Message::ConfirmRemove));
let cancel = widget::button::standard(fl!("cancel"))
@ -711,7 +711,11 @@ fn selected_printer_section() -> Section<crate::pages::Message> {
.apply(Element::from),
);
column = column.add(widget::row::with_children(actions).spacing(spacing.space_s));
column = column.add(
widget::flex_row(actions)
.spacing(spacing.space_s)
.width(Length::Fill),
);
column.apply(Element::from)
})

View file

@ -675,6 +675,7 @@ printers-not-accepting = Not accepting jobs
printers-pause = Pause
printers-remove = Remove printer
printers-remove-confirm = Remove this printer?
printers-remove-confirm-button = Remove
printers-remove-confirm-description = “{ $name }” will be removed from this system. Print jobs queued on it will be discarded.
printers-resume = Resume
printers-state = State

View file

@ -463,6 +463,7 @@ printers-not-accepting = N'accepte pas les travaux
printers-pause = Suspendre
printers-remove = Supprimer l'imprimante
printers-remove-confirm = Supprimer cette imprimante ?
printers-remove-confirm-button = Supprimer
printers-remove-confirm-description = « { $name } » sera supprimée du système. Les travaux en attente sur cette imprimante seront abandonnés.
printers-resume = Reprendre
printers-state = État