|
4 | 4 | import QtQuick |
5 | 5 | import QtQuick.Controls as Controls |
6 | 6 | import QtQuick.Layouts |
| 7 | +import QtQuick.Dialogs as Dialogs |
7 | 8 | import org.kde.kirigami as Kirigami |
8 | 9 |
|
9 | 10 | // DocumentComparePage — two-pane text-diff view for OCR/document compare. |
@@ -41,6 +42,67 @@ Controls.Pane { |
41 | 42 | property int progressTotal: 0 |
42 | 43 | property string progressMessage: "" |
43 | 44 |
|
| 45 | + component FilePickerBar: RowLayout { |
| 46 | + id: fp |
| 47 | + |
| 48 | + property string label: "" |
| 49 | + property string path: "" |
| 50 | + signal browseClicked() |
| 51 | + |
| 52 | + Layout.fillWidth: true |
| 53 | + Layout.preferredHeight: 36 |
| 54 | + spacing: 6 |
| 55 | + |
| 56 | + AppTextField { |
| 57 | + Layout.fillWidth: true |
| 58 | + implicitHeight: 36 |
| 59 | + readOnly: true |
| 60 | + text: fp.path |
| 61 | + placeholderText: fp.label + qsTr(" document path") |
| 62 | + Accessible.name: fp.label + " document path" |
| 63 | + color: root.activeText |
| 64 | + placeholderTextColor: root.activeDisabledText |
| 65 | + background: Rectangle { |
| 66 | + color: root.activeBg |
| 67 | + border.color: root.separatorColor |
| 68 | + border.width: 1 |
| 69 | + radius: 4 |
| 70 | + } |
| 71 | + } |
| 72 | + Controls.ToolButton { |
| 73 | + icon.name: "document-open-folder" |
| 74 | + icon.color: root.activeText |
| 75 | + Controls.ToolTip.text: qsTr("Browse %1 document").arg(fp.label.toLowerCase()) |
| 76 | + Controls.ToolTip.visible: hovered |
| 77 | + Accessible.name: qsTr("Browse %1 document").arg(fp.label) |
| 78 | + onClicked: fp.browseClicked() |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + property var documentNameFilters: [ |
| 83 | + qsTr("Documents (*.pdf *.odt *.docx *.txt *.rtf)"), |
| 84 | + qsTr("All files (*)") |
| 85 | + ] |
| 86 | + |
| 87 | + function urlToLocalPath(u) { |
| 88 | + var path = u.toString().replace(/^file:\/\//, ""); |
| 89 | + return decodeURIComponent(path); |
| 90 | + } |
| 91 | + |
| 92 | + Dialogs.FileDialog { |
| 93 | + id: leftDocumentDialog |
| 94 | + title: qsTr("Select left document") |
| 95 | + nameFilters: root.documentNameFilters |
| 96 | + onAccepted: root.leftPath = root.urlToLocalPath(selectedFile) |
| 97 | + } |
| 98 | + |
| 99 | + Dialogs.FileDialog { |
| 100 | + id: rightDocumentDialog |
| 101 | + title: qsTr("Select right document") |
| 102 | + nameFilters: root.documentNameFilters |
| 103 | + onAccepted: root.rightPath = root.urlToLocalPath(selectedFile) |
| 104 | + } |
| 105 | + |
44 | 106 | // ── Bridge helper ───────────────────────────────────────────────────────── |
45 | 107 | function bridgeGet(path, onLoad) { |
46 | 108 | if (root.bridgeUrl === "") { |
@@ -196,103 +258,110 @@ Controls.Pane { |
196 | 258 | // ── Layout ──────────────────────────────────────────────────────────────── |
197 | 259 | ColumnLayout { |
198 | 260 | anchors.fill: parent |
199 | | - anchors.margins: 8 |
200 | | - spacing: 8 |
| 261 | + spacing: 0 |
201 | 262 |
|
202 | | - // ── Toolbar ────────────────────────────────────────────────────────── |
203 | | - // Settings grouped in a bordered card (matching Image Compare), then |
204 | | - // the primary Run Compare action, a busy indicator, and a right- |
205 | | - // aligned result chip. |
206 | | - RowLayout { |
| 263 | + Rectangle { |
207 | 264 | Layout.fillWidth: true |
208 | | - spacing: 12 |
209 | | - |
210 | | - // Extraction-settings group |
211 | | - Rectangle { |
212 | | - Layout.preferredHeight: 40 |
213 | | - Layout.preferredWidth: docGroupRow.implicitWidth + 20 |
214 | | - color: root.activeBgAlt |
215 | | - border.color: root.separatorColor |
216 | | - border.width: 1 |
217 | | - radius: 6 |
218 | | - |
219 | | - RowLayout { |
220 | | - id: docGroupRow |
221 | | - anchors.fill: parent |
222 | | - anchors.leftMargin: 10 |
223 | | - anchors.rightMargin: 10 |
224 | | - spacing: 10 |
225 | | - |
226 | | - Controls.Label { |
227 | | - text: qsTr("Mode") |
228 | | - color: root.activeDisabledText |
229 | | - font.pixelSize: 11 |
230 | | - } |
231 | | - AppComboBox { |
232 | | - id: modeCombo |
233 | | - model: ["Text", "OCR Text"] |
234 | | - currentIndex: 0 |
235 | | - Layout.preferredWidth: 130 |
236 | | - implicitHeight: 30 |
237 | | - Accessible.name: "Document extraction mode" |
238 | | - } |
| 265 | + Layout.preferredHeight: 54 |
| 266 | + color: root.activeBg |
| 267 | + border.color: root.separatorColor |
| 268 | + border.width: 1 |
239 | 269 |
|
240 | | - Rectangle { |
241 | | - Layout.preferredWidth: 1 |
242 | | - Layout.fillHeight: true |
243 | | - Layout.topMargin: 8 |
244 | | - Layout.bottomMargin: 8 |
245 | | - color: root.separatorColor |
246 | | - } |
| 270 | + RowLayout { |
| 271 | + anchors.fill: parent |
| 272 | + anchors.margins: 8 |
| 273 | + spacing: 8 |
247 | 274 |
|
248 | | - Controls.Label { |
249 | | - text: qsTr("OCR Language") |
250 | | - color: modeCombo.currentIndex === 1 ? root.activeText : root.activeDisabledText |
251 | | - font.pixelSize: 11 |
252 | | - } |
253 | | - AppTextField { |
254 | | - id: ocrLangField |
255 | | - text: "eng" |
256 | | - Layout.preferredWidth: 80 |
257 | | - enabled: modeCombo.currentIndex === 1 |
258 | | - opacity: modeCombo.currentIndex === 1 ? 1.0 : 0.4 |
259 | | - Accessible.name: "OCR language code" |
260 | | - } |
| 275 | + FilePickerBar { |
| 276 | + label: qsTr("Left") |
| 277 | + path: root.leftPath |
| 278 | + onBrowseClicked: leftDocumentDialog.open() |
| 279 | + } |
| 280 | + FilePickerBar { |
| 281 | + label: qsTr("Right") |
| 282 | + path: root.rightPath |
| 283 | + onBrowseClicked: rightDocumentDialog.open() |
261 | 284 | } |
262 | 285 | } |
| 286 | + } |
263 | 287 |
|
264 | | - // Primary action: Run Compare |
265 | | - AppButton { |
266 | | - Layout.preferredHeight: 40 |
267 | | - Layout.preferredWidth: 150 |
268 | | - text: root.running ? qsTr("Comparing…") : qsTr("Run Compare") |
269 | | - icon.name: "media-playback-start" |
270 | | - enabled: !root.running |
271 | | - onClicked: root.runCompare() |
272 | | - } |
| 288 | + Rectangle { |
| 289 | + Layout.fillWidth: true |
| 290 | + Layout.preferredHeight: 40 |
| 291 | + color: root.activeBgAlt |
| 292 | + border.color: root.separatorColor |
| 293 | + border.width: 1 |
273 | 294 |
|
274 | | - Controls.BusyIndicator { |
275 | | - running: root.running |
276 | | - visible: root.running |
277 | | - Layout.preferredWidth: 28 |
278 | | - Layout.preferredHeight: 28 |
279 | | - } |
| 295 | + RowLayout { |
| 296 | + anchors.fill: parent |
| 297 | + anchors.leftMargin: 8 |
| 298 | + anchors.rightMargin: 8 |
| 299 | + spacing: 8 |
280 | 300 |
|
281 | | - Controls.ProgressBar { |
282 | | - visible: root.running && root.progressTotal > 0 |
283 | | - from: 0 |
284 | | - to: root.progressTotal |
285 | | - value: root.progressCurrent |
286 | | - Layout.preferredWidth: 120 |
287 | | - Layout.preferredHeight: 16 |
288 | | - } |
| 301 | + Controls.Label { |
| 302 | + text: qsTr("Mode:") |
| 303 | + color: root.activeText |
| 304 | + opacity: 0.7 |
| 305 | + font.pixelSize: 12 |
| 306 | + } |
| 307 | + AppComboBox { |
| 308 | + id: modeCombo |
| 309 | + model: ["Text", "OCR Text"] |
| 310 | + currentIndex: 0 |
| 311 | + Layout.preferredWidth: 130 |
| 312 | + implicitHeight: 30 |
| 313 | + Accessible.name: "Document extraction mode" |
| 314 | + } |
289 | 315 |
|
290 | | - Item { Layout.fillWidth: true } |
| 316 | + Kirigami.Separator { Layout.fillHeight: true } |
291 | 317 |
|
292 | | - Controls.Label { |
293 | | - text: root.lastResult !== null ? (root.lastResult.equal ? qsTr("Equal") : root.lastResult.differing_lines + qsTr(" diffs")) : "" |
294 | | - color: root.lastResult !== null && !root.lastResult.equal ? "#e53935" : root.activeText |
295 | | - font.bold: true |
| 318 | + Controls.Label { |
| 319 | + text: qsTr("OCR Language:") |
| 320 | + color: modeCombo.currentIndex === 1 ? root.activeText : root.activeDisabledText |
| 321 | + opacity: modeCombo.currentIndex === 1 ? 0.7 : 1.0 |
| 322 | + font.pixelSize: 12 |
| 323 | + } |
| 324 | + AppTextField { |
| 325 | + id: ocrLangField |
| 326 | + text: "eng" |
| 327 | + Layout.preferredWidth: 80 |
| 328 | + enabled: modeCombo.currentIndex === 1 |
| 329 | + opacity: modeCombo.currentIndex === 1 ? 1.0 : 0.4 |
| 330 | + Accessible.name: "OCR language code" |
| 331 | + } |
| 332 | + |
| 333 | + AppButton { |
| 334 | + Layout.preferredHeight: 30 |
| 335 | + Layout.preferredWidth: 150 |
| 336 | + text: root.running ? qsTr("Comparing…") : qsTr("Run Compare") |
| 337 | + icon.name: "media-playback-start" |
| 338 | + enabled: !root.running && root.leftPath !== "" && root.rightPath !== "" |
| 339 | + onClicked: root.runCompare() |
| 340 | + } |
| 341 | + |
| 342 | + Controls.BusyIndicator { |
| 343 | + running: root.running |
| 344 | + visible: root.running |
| 345 | + Layout.preferredWidth: 28 |
| 346 | + Layout.preferredHeight: 28 |
| 347 | + } |
| 348 | + |
| 349 | + Controls.ProgressBar { |
| 350 | + visible: root.running && root.progressTotal > 0 |
| 351 | + from: 0 |
| 352 | + to: root.progressTotal |
| 353 | + value: root.progressCurrent |
| 354 | + Layout.preferredWidth: 120 |
| 355 | + Layout.preferredHeight: 16 |
| 356 | + } |
| 357 | + |
| 358 | + Item { Layout.fillWidth: true } |
| 359 | + |
| 360 | + Controls.Label { |
| 361 | + text: root.lastResult !== null ? (root.lastResult.equal ? qsTr("Equal") : root.lastResult.differing_lines + qsTr(" diffs")) : "" |
| 362 | + color: root.lastResult !== null && !root.lastResult.equal ? "#e53935" : root.activeText |
| 363 | + font.bold: true |
| 364 | + } |
296 | 365 | } |
297 | 366 | } |
298 | 367 |
|
|
0 commit comments