Own the network: details, VPN, enterprise Wi-Fi, and a firewall that can also allow
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -29,6 +29,43 @@ SettingsPage {
|
||||
readonly property bool manualReady: /^(ipp|ipps|socket):\/\/\S+$/.test(root.manualUri)
|
||||
&& root.manualName.trim() !== ""
|
||||
|
||||
// The two defaults a queue can be told to change, dressed for reading. The
|
||||
// values are the helper's closed vocabulary and nothing else reaches
|
||||
// lpadmin; the choices come from what the printer said it supports, so a
|
||||
// queue that cannot print two-sided never grows a two-sided dropdown.
|
||||
//
|
||||
// Empty while get-options is still on its way, which is why the rows are
|
||||
// gated on the list rather than on the current value: an empty dropdown
|
||||
// above a printer is worse than no dropdown at all.
|
||||
function mediaChoices(reported: var): var {
|
||||
const detail = {
|
||||
"Letter": "8.5 × 11 in",
|
||||
"A4": "210 × 297 mm",
|
||||
"Legal": "8.5 × 14 in"
|
||||
};
|
||||
return (reported?.choices?.media ?? []).map(choice => ({
|
||||
value: String(choice),
|
||||
label: String(choice),
|
||||
detail: detail[String(choice)] ?? ""
|
||||
}));
|
||||
}
|
||||
|
||||
function sidesChoices(reported: var): var {
|
||||
const named = {
|
||||
"one-sided": { label: "Off", detail: "One page per sheet" },
|
||||
"two-sided-long-edge": { label: "Long edge", detail: "Bound like a book" },
|
||||
"two-sided-short-edge": { label: "Short edge", detail: "Bound like a notepad" }
|
||||
};
|
||||
return (reported?.choices?.sides ?? []).map(choice => {
|
||||
const known = named[String(choice)];
|
||||
return {
|
||||
value: String(choice),
|
||||
label: known ? known.label : String(choice),
|
||||
detail: known ? known.detail : ""
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Component.onCompleted: Printers.refresh()
|
||||
|
||||
TextRow {
|
||||
@@ -80,9 +117,18 @@ SettingsPage {
|
||||
}
|
||||
|
||||
Column {
|
||||
id: printerBody
|
||||
|
||||
width: printerBlock.width
|
||||
visible: printerBlock.open
|
||||
|
||||
// What this queue is set to, as CUPS reports it. Null until
|
||||
// the read comes back, and a printer that reports neither
|
||||
// key simply has no dropdowns -- an option offered for a
|
||||
// printer that does not have it is a setting that silently
|
||||
// does nothing.
|
||||
readonly property var reported: Printers.optionsFor(printerBlock.name)
|
||||
|
||||
TextRow {
|
||||
width: printerBlock.width
|
||||
label: "Model"
|
||||
@@ -90,6 +136,28 @@ SettingsPage {
|
||||
value: String(printerBlock.modelData.makeAndModel ?? "Unknown")
|
||||
}
|
||||
|
||||
OptionPickerRow {
|
||||
width: printerBlock.width
|
||||
visible: root.mediaChoices(printerBody.reported).length > 0
|
||||
label: "Paper size"
|
||||
detail: "What applications print onto unless they ask for something else"
|
||||
enabled: !Printers.busy
|
||||
options: root.mediaChoices(printerBody.reported)
|
||||
current: String(printerBody.reported?.options?.media ?? "")
|
||||
onPicked: value => Printers.setOption(printerBlock.name, "media", String(value))
|
||||
}
|
||||
|
||||
OptionPickerRow {
|
||||
width: printerBlock.width
|
||||
visible: root.sidesChoices(printerBody.reported).length > 0
|
||||
label: "Two-sided"
|
||||
detail: "Long edge is the usual choice for text; short edge flips like a notepad"
|
||||
enabled: !Printers.busy
|
||||
options: root.sidesChoices(printerBody.reported)
|
||||
current: String(printerBody.reported?.options?.sides ?? "")
|
||||
onPicked: value => Printers.setOption(printerBlock.name, "sides", String(value))
|
||||
}
|
||||
|
||||
ActionRow {
|
||||
width: printerBlock.width
|
||||
visible: !printerBlock.modelData.isDefault
|
||||
@@ -173,22 +241,47 @@ SettingsPage {
|
||||
Repeater {
|
||||
model: Printers.jobs
|
||||
|
||||
delegate: ActionRow {
|
||||
delegate: SettingRow {
|
||||
id: jobRow
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
readonly property bool held: Printers.isHeld(jobRow.modelData)
|
||||
|
||||
width: parent.width
|
||||
label: String(jobRow.modelData.name ?? "Untitled")
|
||||
detail: String(jobRow.modelData.printer ?? "") + " · "
|
||||
+ String(jobRow.modelData.state ?? "")
|
||||
+ (Number(jobRow.modelData.pages ?? 0) > 0
|
||||
? " · " + jobRow.modelData.pages + " pages" : "")
|
||||
action: "Cancel"
|
||||
enabled: !Printers.busy
|
||||
controlWidth: 175
|
||||
divider: jobRow.index < Printers.jobs.length - 1
|
||||
onTriggered: Printers.cancel(Number(jobRow.modelData.id))
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 8
|
||||
|
||||
// Holding is the answer to "stop this one, I have not
|
||||
// finished with it" -- cancelling was the only way out of
|
||||
// the queue before, and it is not reversible.
|
||||
SettingsButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: jobRow.held ? "Release" : "Hold"
|
||||
enabled: !Printers.busy
|
||||
onClicked: jobRow.held
|
||||
? Printers.release(Number(jobRow.modelData.id))
|
||||
: Printers.hold(Number(jobRow.modelData.id))
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Cancel"
|
||||
enabled: !Printers.busy
|
||||
onClicked: Printers.cancel(Number(jobRow.modelData.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,42 +294,26 @@ SettingsPage {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Nothing set up yet ───────────────────────────────────────────────────
|
||||
|
||||
SettingsCard {
|
||||
visible: Printers.scanned && !Printers.anyPrinters
|
||||
title: "No printers yet"
|
||||
subtitle: "Printers that announce themselves on your network appear here on their own."
|
||||
|
||||
ActionRow {
|
||||
label: "Search the network"
|
||||
detail: Printers.searching
|
||||
? "Listening for printers that announce themselves…"
|
||||
: (Printers.searched
|
||||
? Printers.addable.length + " found"
|
||||
: "Looks for printers over mDNS, the same way phones and laptops find them")
|
||||
action: Printers.searching ? "Searching…" : "Search"
|
||||
enabled: !Printers.searching
|
||||
divider: false
|
||||
onTriggered: Printers.search()
|
||||
}
|
||||
}
|
||||
|
||||
// ── Adding ───────────────────────────────────────────────────────────────
|
||||
//
|
||||
// One card, not two. "No printers yet" and "Add a printer" carried the same
|
||||
// Search row with two different wordings, so a machine with no printers
|
||||
// offered the identical button twice under two headings.
|
||||
|
||||
SettingsCard {
|
||||
title: "Add a printer"
|
||||
subtitle: "Driverless printers only. One that needs a manufacturer driver has to be set up with the system printer tool."
|
||||
title: Printers.anyPrinters ? "Add a printer" : "No printers yet"
|
||||
subtitle: Printers.anyPrinters
|
||||
? "Driverless printers only. One that needs a manufacturer driver has to be set up with the system printer tool."
|
||||
: "Printers on this network that speak modern IPP appear here on their own. Driverless only."
|
||||
|
||||
ActionRow {
|
||||
visible: Printers.anyPrinters
|
||||
label: "Search the network"
|
||||
detail: Printers.searching
|
||||
? "Listening for printers that announce themselves…"
|
||||
: (Printers.searched
|
||||
? Printers.addable.length + " printer"
|
||||
+ (Printers.addable.length === 1 ? "" : "s") + " found that are not set up here"
|
||||
: "Looks for printers over mDNS")
|
||||
: "Looks for printers over mDNS, the same way phones and laptops find them")
|
||||
action: Printers.searching ? "Searching…" : "Search"
|
||||
enabled: !Printers.searching
|
||||
onTriggered: Printers.search()
|
||||
|
||||
Reference in New Issue
Block a user