From f92bf115994fd251bb1bba3bc223468f0ec3efb2 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 20 Mar 2026 18:03:24 +0100 Subject: [PATCH 01/40] added a draft version of a new selectable table view --- src/EasyApp/Gui/Components/NewTableView.qml | 132 ++++++++++++++++++++ src/EasyApp/Gui/Components/TableView.qml | 1 + src/EasyApp/Gui/Components/qmldir | 1 + 3 files changed, 134 insertions(+) create mode 100644 src/EasyApp/Gui/Components/NewTableView.qml diff --git a/src/EasyApp/Gui/Components/NewTableView.qml b/src/EasyApp/Gui/Components/NewTableView.qml new file mode 100644 index 00000000..9d0eb8e9 --- /dev/null +++ b/src/EasyApp/Gui/Components/NewTableView.qml @@ -0,0 +1,132 @@ +import QtQuick +import QtQuick.Controls + +import EasyApp.Gui.Globals as EaGlobals +import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Animations as EaAnimations +import EasyApp.Gui.Elements as EaElements +import EasyApp.Gui.Components as EaComponents + +Item { + id: newTableView + height: 200 + width: EaStyle.Sizes.sideBarContentWidth + + // exposing underlying tableview API + property alias showHeader: nestedTableView.showHeader + property alias tallRows: nestedTableView.tallRows + property alias maxRowCountShow: nestedTableView.maxRowCountShow + property alias defaultInfoText: nestedTableView.defaultInfoText + property alias header: nestedTableView.header + property alias model: nestedTableView.model + property alias delegate: nestedTableView.delegate + + ItemSelectionModel { + id: selectionModel + model: nestedTableView.model + } + + // --- helper: convert row -> QModelIndex --- + function _index(row) { + if (!selectionModel.model) + return null + return selectionModel.model.index(row, 0) + } + + // --- public API --- + function isSelected(row) { + let idx = _index(row) + return idx ? selectionModel.isSelected(idx) : false + } + + function select(row) { + let idx = _index(row) + if (!idx) + return + + selectionModel.select( + idx, + ItemSelectionModel.Select | ItemSelectionModel.Rows + ) + } + + function selectSingle(row) { + let idx = _index(row) + if (!idx) + return + + selectionModel.clearSelection() + selectionModel.select( + idx, + ItemSelectionModel.Select | ItemSelectionModel.Rows + ) + } + + function toggleSelection(row) { + let idx = _index(row) + if (!idx) + return + + if (selectionModel.isSelected(idx)) { + selectionModel.select( + idx, + ItemSelectionModel.Deselect | ItemSelectionModel.Rows + ) + } else { + selectionModel.select( + idx, + ItemSelectionModel.Select | ItemSelectionModel.Rows + ) + } + } + + function clearSelection() { + selectionModel.clearSelection() + } + + + EaComponents.TableView { + id: nestedTableView + clip: true + antialiasing: true + anchors.fill: parent + anchors.margins: 1 + + delegate: EaComponents.TableViewDelegate { + + required property int index + required property string name + required property string structure_type + required property string description + + color: newTableView.isSelected(index) + ? EaStyle.Colors.themeAccentMinor + : (index % 2 + ? EaStyle.Colors.themeBackgroundHovered2 + : EaStyle.Colors.themeBackgroundHovered1) + + EaComponents.TableViewLabel { + id: modelNameColumn + width: EaStyle.Sizes.fontPixelSize * 10 + text: name + leftPadding: EaStyle.Sizes.fontPixelSize * 0.7 + } + + EaComponents.TableViewLabel { + id: typeColumn + width: EaStyle.Sizes.fontPixelSize * 6 + text: structure_type + } + + EaComponents.TableViewLabel { + id: descrColumn + width: EaStyle.Sizes.fontPixelSize * 22 + text: description + } + + mouseArea.onPressed: (mouse) => { + newTableView.select(index) + } + } + } +} diff --git a/src/EasyApp/Gui/Components/TableView.qml b/src/EasyApp/Gui/Components/TableView.qml index c3d538ec..bd02319f 100644 --- a/src/EasyApp/Gui/Components/TableView.qml +++ b/src/EasyApp/Gui/Components/TableView.qml @@ -57,6 +57,7 @@ ListView { Rectangle { anchors.fill: listView color: "transparent" + //antialiasing: true border.color: EaStyle.Colors.appBarComboBoxBorder Behavior on border.color { EaAnimations.ThemeChange {} } } diff --git a/src/EasyApp/Gui/Components/qmldir b/src/EasyApp/Gui/Components/qmldir index 55a7dd24..608c090a 100644 --- a/src/EasyApp/Gui/Components/qmldir +++ b/src/EasyApp/Gui/Components/qmldir @@ -21,6 +21,7 @@ SideBarColumn 1.0 SideBarColumn.qml PreferencesDialog 1.0 PreferencesDialog.qml ProjectDescriptionDialog 1.0 ProjectDescriptionDialog.qml +NewTableView 1.0 NewTableView.qml TableView 1.0 TableView.qml TableViewHeader 1.0 TableViewHeader.qml TableViewDelegate 1.0 TableViewDelegate.qml From 50f4f4891c8a42f3d567a127dd1672ae654091d0 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Sun, 22 Mar 2026 15:08:56 +0100 Subject: [PATCH 02/40] Removed delegate to a separate file --- src/EasyApp/Gui/Components/NewTableView.qml | 98 +++++++++++-------- .../Gui/Components/NewTableViewDelegate.qml | 63 ++++++++++++ src/EasyApp/Gui/Components/qmldir | 1 + 3 files changed, 121 insertions(+), 41 deletions(-) create mode 100644 src/EasyApp/Gui/Components/NewTableViewDelegate.qml diff --git a/src/EasyApp/Gui/Components/NewTableView.qml b/src/EasyApp/Gui/Components/NewTableView.qml index 9d0eb8e9..05d38b3f 100644 --- a/src/EasyApp/Gui/Components/NewTableView.qml +++ b/src/EasyApp/Gui/Components/NewTableView.qml @@ -9,10 +9,15 @@ import EasyApp.Gui.Components as EaComponents Item { id: newTableView - height: 200 + height: count === 0 ? + 2 * EaStyle.Sizes.tableRowHeight : + showHeader ? + nestedTableView.tableRowHeight * (Math.min(count, maxRowCountShow) + 1 ) : + nestedTableView.tableRowHeight * (Math.min(count, maxRowCountShow)) width: EaStyle.Sizes.sideBarContentWidth // exposing underlying tableview API + property alias count: nestedTableView.count property alias showHeader: nestedTableView.showHeader property alias tallRows: nestedTableView.tallRows property alias maxRowCountShow: nestedTableView.maxRowCountShow @@ -21,11 +26,24 @@ Item { property alias model: nestedTableView.model property alias delegate: nestedTableView.delegate + // trigger for bindings + property int selectionRevision: 0 + // idx for shift-selection + property int anchorRow: -1 + ItemSelectionModel { id: selectionModel model: nestedTableView.model } + Connections { + target: selectionModel + + function onSelectionChanged() { + newTableView.selectionRevision++ + } + } + // --- helper: convert row -> QModelIndex --- function _index(row) { if (!selectionModel.model) @@ -39,45 +57,53 @@ Item { return idx ? selectionModel.isSelected(idx) : false } - function select(row) { + function selectWithModifiers(row, modifiers) { let idx = _index(row) - if (!idx) - return + if (!idx) return - selectionModel.select( - idx, - ItemSelectionModel.Select | ItemSelectionModel.Rows - ) - } + // --- SHIFT: range selection --- + if (modifiers & Qt.ShiftModifier) { + if (anchorRow < 0) { + anchorRow = row + } - function selectSingle(row) { - let idx = _index(row) - if (!idx) - return + let from = Math.min(anchorRow, row) + let to = Math.max(anchorRow, row) - selectionModel.clearSelection() - selectionModel.select( - idx, - ItemSelectionModel.Select | ItemSelectionModel.Rows - ) - } + // If Ctrl is NOT pressed → replace selection + if (!(modifiers & Qt.ControlModifier)) { + selectionModel.clearSelection() + } + + for (let i = from; i <= to; i++) { + let rIdx = _index(i) + if (rIdx) { + selectionModel.select( + rIdx, + ItemSelectionModel.Select | ItemSelectionModel.Rows + ) + } + } - function toggleSelection(row) { - let idx = _index(row) - if (!idx) return + } - if (selectionModel.isSelected(idx)) { + // --- CTRL: toggle --- + if (modifiers & Qt.ControlModifier) { selectionModel.select( idx, - ItemSelectionModel.Deselect | ItemSelectionModel.Rows - ) - } else { - selectionModel.select( - idx, - ItemSelectionModel.Select | ItemSelectionModel.Rows + ItemSelectionModel.Toggle | ItemSelectionModel.Rows ) + anchorRow = row + return } + + // --- DEFAULT: single selection --- + selectionModel.select( + idx, + ItemSelectionModel.ClearAndSelect | ItemSelectionModel.Rows + ) + anchorRow = row } function clearSelection() { @@ -92,18 +118,12 @@ Item { anchors.fill: parent anchors.margins: 1 - delegate: EaComponents.TableViewDelegate { - + delegate: EaComponents.NewTableViewDelegate { required property int index required property string name required property string structure_type required property string description - - color: newTableView.isSelected(index) - ? EaStyle.Colors.themeAccentMinor - : (index % 2 - ? EaStyle.Colors.themeBackgroundHovered2 - : EaStyle.Colors.themeBackgroundHovered1) + tableView: nestedTableView EaComponents.TableViewLabel { id: modelNameColumn @@ -123,10 +143,6 @@ Item { width: EaStyle.Sizes.fontPixelSize * 22 text: description } - - mouseArea.onPressed: (mouse) => { - newTableView.select(index) - } } } } diff --git a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml new file mode 100644 index 00000000..36aea81e --- /dev/null +++ b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml @@ -0,0 +1,63 @@ +import QtQuick + +import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Animations as EaAnimations + +Rectangle { + id: control + + default property alias contentRowData: contentRow.data + property alias mouseArea: mouseArea + property Item tableView: parent === null ? null : parent.parent + + implicitWidth: parent == null ? 0 : parent.width + implicitHeight: tableView === null ? EaStyle.Sizes.tableRowHeight : tableView.tableRowHeight + + color: { + newTableView.selectionRevision + + let selected = newTableView.isSelected(index) + let c1 = EaStyle.Colors.themeAccentMinor || "lightblue" + let c2 = EaStyle.Colors.themeBackgroundHovered2 || "#eeeeee" + let c3 = EaStyle.Colors.themeBackgroundHovered1 || "#dddddd" + + return selected + ? c1 + : (index % 2 ? c2 : c3) + } + Behavior on color { EaAnimations.ThemeChange {} } + + Row { + id: contentRow + + height: parent.height + spacing: EaStyle.Sizes.tableColumnSpacing + } + + //Mouse area to react on click events + MouseArea { + id: mouseArea + anchors.fill: parent + propagateComposedEvents: true + cursorShape: undefined //Qt.PointingHandCursor + hoverEnabled: false + onPressed: (mouse) => { + parent.ListView.view.selectWithModifiers(index, mouse.modifiers) + } + } + + // HoverHandler to react on hover events + HoverHandler { + id: mouseHoverHandler + acceptedDevices: PointerDevice.AllDevices + cursorShape: Qt.PointingHandCursor + blocking: false + onHoveredChanged: { + if (hovered) { + //console.error(`${control} [TableViewDelegate.qml] hovered`) + parent.ListView.view.currentIndex = index + } + } + } + +} diff --git a/src/EasyApp/Gui/Components/qmldir b/src/EasyApp/Gui/Components/qmldir index 608c090a..7437af08 100644 --- a/src/EasyApp/Gui/Components/qmldir +++ b/src/EasyApp/Gui/Components/qmldir @@ -22,6 +22,7 @@ PreferencesDialog 1.0 PreferencesDialog.qml ProjectDescriptionDialog 1.0 ProjectDescriptionDialog.qml NewTableView 1.0 NewTableView.qml +NewTableViewDelegate 1.0 NewTableViewDelegate.qml TableView 1.0 TableView.qml TableViewHeader 1.0 TableViewHeader.qml TableViewDelegate 1.0 TableViewDelegate.qml From a947f3d29c4d9847b3e426bab12a02f177e5c985 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 23 Mar 2026 13:28:39 +0100 Subject: [PATCH 03/40] delegate not working fix --- src/EasyApp/Gui/Components/NewTableView.qml | 1 - src/EasyApp/Gui/Components/NewTableViewDelegate.qml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/EasyApp/Gui/Components/NewTableView.qml b/src/EasyApp/Gui/Components/NewTableView.qml index 05d38b3f..d9e2f308 100644 --- a/src/EasyApp/Gui/Components/NewTableView.qml +++ b/src/EasyApp/Gui/Components/NewTableView.qml @@ -123,7 +123,6 @@ Item { required property string name required property string structure_type required property string description - tableView: nestedTableView EaComponents.TableViewLabel { id: modelNameColumn diff --git a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml index 36aea81e..b74dbb6a 100644 --- a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml +++ b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml @@ -17,7 +17,7 @@ Rectangle { newTableView.selectionRevision let selected = newTableView.isSelected(index) - let c1 = EaStyle.Colors.themeAccentMinor || "lightblue" + let c1 = EaStyle.Colors.themeAccentMinor || "#4d9dbd" let c2 = EaStyle.Colors.themeBackgroundHovered2 || "#eeeeee" let c3 = EaStyle.Colors.themeBackgroundHovered1 || "#dddddd" @@ -42,7 +42,7 @@ Rectangle { cursorShape: undefined //Qt.PointingHandCursor hoverEnabled: false onPressed: (mouse) => { - parent.ListView.view.selectWithModifiers(index, mouse.modifiers) + control.ListView.view.parent.selectWithModifiers(index, mouse.modifiers) } } From 27240b0c305627de79ab51926bcd5b6e7fd9e541 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 23 Mar 2026 14:42:10 +0100 Subject: [PATCH 04/40] removed the initialized delegate from NewTableView component --- src/EasyApp/Gui/Components/NewTableView.qml | 24 ------------------- .../Gui/Components/NewTableViewDelegate.qml | 8 +++---- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/EasyApp/Gui/Components/NewTableView.qml b/src/EasyApp/Gui/Components/NewTableView.qml index d9e2f308..db42847f 100644 --- a/src/EasyApp/Gui/Components/NewTableView.qml +++ b/src/EasyApp/Gui/Components/NewTableView.qml @@ -118,30 +118,6 @@ Item { anchors.fill: parent anchors.margins: 1 - delegate: EaComponents.NewTableViewDelegate { - required property int index - required property string name - required property string structure_type - required property string description - - EaComponents.TableViewLabel { - id: modelNameColumn - width: EaStyle.Sizes.fontPixelSize * 10 - text: name - leftPadding: EaStyle.Sizes.fontPixelSize * 0.7 - } - - EaComponents.TableViewLabel { - id: typeColumn - width: EaStyle.Sizes.fontPixelSize * 6 - text: structure_type - } - EaComponents.TableViewLabel { - id: descrColumn - width: EaStyle.Sizes.fontPixelSize * 22 - text: description - } - } } } diff --git a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml index b74dbb6a..c1d30f4a 100644 --- a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml +++ b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml @@ -14,10 +14,11 @@ Rectangle { implicitHeight: tableView === null ? EaStyle.Sizes.tableRowHeight : tableView.tableRowHeight color: { - newTableView.selectionRevision + ListView.view.parent.selectionRevision - let selected = newTableView.isSelected(index) - let c1 = EaStyle.Colors.themeAccentMinor || "#4d9dbd" + let selected = ListView.view.parent.isSelected(index) + // quickfix until new color accent PR is accepted + let c1 = EaStyle.Colors.themeAccentMinor || "#4d9dbd" // "#8ad6ed" for light or "#4d9dbd" for dark let c2 = EaStyle.Colors.themeBackgroundHovered2 || "#eeeeee" let c3 = EaStyle.Colors.themeBackgroundHovered1 || "#dddddd" @@ -59,5 +60,4 @@ Rectangle { } } } - } From 8c86671bfb758e36625a16e83166a571b020f4b4 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 24 Mar 2026 11:54:59 +0100 Subject: [PATCH 05/40] changes to the scrollbar --- src/EasyApp/Gui/Components/NewTableView.qml | 38 ++++++++++++++++++- .../Gui/Components/NewTableViewDelegate.qml | 13 +++++-- src/EasyApp/Gui/Components/TableView.qml | 2 +- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/EasyApp/Gui/Components/NewTableView.qml b/src/EasyApp/Gui/Components/NewTableView.qml index db42847f..8fbc8a69 100644 --- a/src/EasyApp/Gui/Components/NewTableView.qml +++ b/src/EasyApp/Gui/Components/NewTableView.qml @@ -110,14 +110,48 @@ Item { selectionModel.clearSelection() } + // ScrollView{ + // width: nestedTableView.width + // height: nestedTableView.height + + // ScrollBar.vertical: EaElements.ScrollBar { + // id: scrollBar + // anchors.right: parent.right + // // anchors.top: parent.header.bottom + // topPadding: parent.showHeader ? parent.tableRowHeight : 0 + // background.anchors.top: parent.parent.header.bottom + // //anchors.bottom: parent.bottom + // policy: ScrollBar.AlwaysOn // ScrollBar.AsNeeded + // width: 6 + // } EaComponents.TableView { id: nestedTableView clip: true antialiasing: true - anchors.fill: parent - anchors.margins: 1 + anchors { + fill: parent + margins: 1 + // rightMargin: 1 // scrollBar.width + } + + ScrollBar.vertical: EaElements.ScrollBar { + id: scrollBar + // anchors.right: parent.right + // anchors.top: parent.header.bottom + topInset: parent.showHeader ? parent.tableRowHeight : 0 + background.anchors.top: parent.parent.header.bottom + //anchors.bottom: parent.bottom + policy: ScrollBar.AsNeeded // ScrollBar.AsNeeded + width: 6 + } + // fixes an issue of clicks not registering right after scroll + // does not give too much delay due to selection animation playing anyway + // somehow value doesn't affect anything, just fixes the missing clicks issue + // even 10000 delay doesn't create a long delay, just fixes the issue + pressDelay: 10 } + } diff --git a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml index c1d30f4a..1ea668ee 100644 --- a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml +++ b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml @@ -7,10 +7,10 @@ Rectangle { id: control default property alias contentRowData: contentRow.data - property alias mouseArea: mouseArea + //property alias mouseArea: mouseArea property Item tableView: parent === null ? null : parent.parent - implicitWidth: parent == null ? 0 : parent.width + implicitWidth: ListView.view.parent.width implicitHeight: tableView === null ? EaStyle.Sizes.tableRowHeight : tableView.tableRowHeight color: { @@ -42,11 +42,18 @@ Rectangle { propagateComposedEvents: true cursorShape: undefined //Qt.PointingHandCursor hoverEnabled: false - onPressed: (mouse) => { + onReleased: (mouse) => { control.ListView.view.parent.selectWithModifiers(index, mouse.modifiers) } } + // TapHandler { + // acceptedButtons: Qt.LeftButton // | Qt.RightButton // match whatever you need + // onTapped: (eventPoint, button) => { + // control.ListView.view.parent.selectWithModifiers(index, eventPoint.modifiers) + // } + // } + // HoverHandler to react on hover events HoverHandler { id: mouseHoverHandler diff --git a/src/EasyApp/Gui/Components/TableView.qml b/src/EasyApp/Gui/Components/TableView.qml index bd02319f..4472dbb9 100644 --- a/src/EasyApp/Gui/Components/TableView.qml +++ b/src/EasyApp/Gui/Components/TableView.qml @@ -57,7 +57,7 @@ ListView { Rectangle { anchors.fill: listView color: "transparent" - //antialiasing: true + antialiasing: true border.color: EaStyle.Colors.appBarComboBoxBorder Behavior on border.color { EaAnimations.ThemeChange {} } } From fb10fa268c7bb06196ed2d7533f9d55d1e8e293a Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 24 Mar 2026 13:41:22 +0100 Subject: [PATCH 06/40] updating scrollbar in the tableview --- src/EasyApp/Gui/Components/NewTableView.qml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/EasyApp/Gui/Components/NewTableView.qml b/src/EasyApp/Gui/Components/NewTableView.qml index 8fbc8a69..86dd0cb7 100644 --- a/src/EasyApp/Gui/Components/NewTableView.qml +++ b/src/EasyApp/Gui/Components/NewTableView.qml @@ -131,7 +131,7 @@ Item { antialiasing: true anchors { fill: parent - margins: 1 + // margins: 1 // rightMargin: 1 // scrollBar.width } @@ -139,10 +139,12 @@ Item { id: scrollBar // anchors.right: parent.right // anchors.top: parent.header.bottom + anchors.topMargin: 1 topInset: parent.showHeader ? parent.tableRowHeight : 0 - background.anchors.top: parent.parent.header.bottom - //anchors.bottom: parent.bottom - policy: ScrollBar.AsNeeded // ScrollBar.AsNeeded + topPadding: parent.showHeader ? parent.tableRowHeight + 1 : 0 + //background.anchors.top: parent.parent.header.bottom + anchors.bottom: parent.bottom + policy: ScrollBar.AlwaysOn // ScrollBar.AsNeeded // AlwaysOn width: 6 } From fd20025d721b4e989a0f90345ffa98806be1e5e0 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 24 Mar 2026 14:36:17 +0100 Subject: [PATCH 07/40] removed the color safeguards in the tableview --- src/EasyApp/Gui/Components/NewTableViewDelegate.qml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml index 1ea668ee..97acc8be 100644 --- a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml +++ b/src/EasyApp/Gui/Components/NewTableViewDelegate.qml @@ -17,14 +17,11 @@ Rectangle { ListView.view.parent.selectionRevision let selected = ListView.view.parent.isSelected(index) - // quickfix until new color accent PR is accepted - let c1 = EaStyle.Colors.themeAccentMinor || "#4d9dbd" // "#8ad6ed" for light or "#4d9dbd" for dark - let c2 = EaStyle.Colors.themeBackgroundHovered2 || "#eeeeee" - let c3 = EaStyle.Colors.themeBackgroundHovered1 || "#dddddd" + let c1 = EaStyle.Colors.themeAccentMinor + let c2 = EaStyle.Colors.themeBackgroundHovered2 + let c3 = EaStyle.Colors.themeBackgroundHovered1 - return selected - ? c1 - : (index % 2 ? c2 : c3) + return selected ? c1 : (index % 2 ? c2 : c3) } Behavior on color { EaAnimations.ThemeChange {} } From 3fcb201d663ca3d9d39546d5f3da98b1ebc97fcf Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 24 Mar 2026 14:37:13 +0100 Subject: [PATCH 08/40] removed antialiasing in tableview as there is another pr for that --- src/EasyApp/Gui/Components/TableView.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EasyApp/Gui/Components/TableView.qml b/src/EasyApp/Gui/Components/TableView.qml index 4472dbb9..c3d538ec 100644 --- a/src/EasyApp/Gui/Components/TableView.qml +++ b/src/EasyApp/Gui/Components/TableView.qml @@ -57,7 +57,6 @@ ListView { Rectangle { anchors.fill: listView color: "transparent" - antialiasing: true border.color: EaStyle.Colors.appBarComboBoxBorder Behavior on border.color { EaAnimations.ThemeChange {} } } From e15579d6867ca7fe7dcce1c693fa8570544c9cd6 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 27 Mar 2026 11:26:47 +0100 Subject: [PATCH 09/40] make scrollbar and scroll indicator optional in ListView via verticalScrollBar/verticalScrollIndicator properties Co-Authored-By: Claude Sonnet 4.6 --- .../{NewTableView.qml => ListView.qml} | 60 ++++++------------- ...eViewDelegate.qml => ListViewDelegate.qml} | 0 src/EasyApp/Gui/Components/qmldir | 4 +- 3 files changed, 21 insertions(+), 43 deletions(-) rename src/EasyApp/Gui/Components/{NewTableView.qml => ListView.qml} (61%) rename src/EasyApp/Gui/Components/{NewTableViewDelegate.qml => ListViewDelegate.qml} (100%) diff --git a/src/EasyApp/Gui/Components/NewTableView.qml b/src/EasyApp/Gui/Components/ListView.qml similarity index 61% rename from src/EasyApp/Gui/Components/NewTableView.qml rename to src/EasyApp/Gui/Components/ListView.qml index 86dd0cb7..5ec8d709 100644 --- a/src/EasyApp/Gui/Components/NewTableView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -8,23 +8,26 @@ import EasyApp.Gui.Elements as EaElements import EasyApp.Gui.Components as EaComponents Item { - id: newTableView + id: listView height: count === 0 ? 2 * EaStyle.Sizes.tableRowHeight : showHeader ? - nestedTableView.tableRowHeight * (Math.min(count, maxRowCountShow) + 1 ) : - nestedTableView.tableRowHeight * (Math.min(count, maxRowCountShow)) + nestedListView.tableRowHeight * (Math.min(count, maxRowCountShow) + 1 ) : + nestedListView.tableRowHeight * (Math.min(count, maxRowCountShow)) width: EaStyle.Sizes.sideBarContentWidth // exposing underlying tableview API - property alias count: nestedTableView.count - property alias showHeader: nestedTableView.showHeader - property alias tallRows: nestedTableView.tallRows - property alias maxRowCountShow: nestedTableView.maxRowCountShow - property alias defaultInfoText: nestedTableView.defaultInfoText - property alias header: nestedTableView.header - property alias model: nestedTableView.model - property alias delegate: nestedTableView.delegate + property alias count: nestedListView.count + property alias showHeader: nestedListView.showHeader + property alias tallRows: nestedListView.tallRows + property alias maxRowCountShow: nestedListView.maxRowCountShow + property alias defaultInfoText: nestedListView.defaultInfoText + property alias header: nestedListView.header + property alias model: nestedListView.model + property alias delegate: nestedListView.delegate + + property ScrollBar verticalScrollBar: null + property ScrollIndicator verticalScrollIndicator: null // trigger for bindings property int selectionRevision: 0 @@ -33,14 +36,14 @@ Item { ItemSelectionModel { id: selectionModel - model: nestedTableView.model + model: nestedListView.model } Connections { target: selectionModel function onSelectionChanged() { - newTableView.selectionRevision++ + listView.selectionRevision++ } } @@ -110,23 +113,8 @@ Item { selectionModel.clearSelection() } - // ScrollView{ - // width: nestedTableView.width - // height: nestedTableView.height - - // ScrollBar.vertical: EaElements.ScrollBar { - // id: scrollBar - // anchors.right: parent.right - // // anchors.top: parent.header.bottom - // topPadding: parent.showHeader ? parent.tableRowHeight : 0 - // background.anchors.top: parent.parent.header.bottom - // //anchors.bottom: parent.bottom - // policy: ScrollBar.AlwaysOn // ScrollBar.AsNeeded - // width: 6 - // } - EaComponents.TableView { - id: nestedTableView + id: nestedListView clip: true antialiasing: true anchors { @@ -135,18 +123,8 @@ Item { // rightMargin: 1 // scrollBar.width } - ScrollBar.vertical: EaElements.ScrollBar { - id: scrollBar - // anchors.right: parent.right - // anchors.top: parent.header.bottom - anchors.topMargin: 1 - topInset: parent.showHeader ? parent.tableRowHeight : 0 - topPadding: parent.showHeader ? parent.tableRowHeight + 1 : 0 - //background.anchors.top: parent.parent.header.bottom - anchors.bottom: parent.bottom - policy: ScrollBar.AlwaysOn // ScrollBar.AsNeeded // AlwaysOn - width: 6 - } + ScrollBar.vertical: listView.verticalScrollBar + ScrollIndicator.vertical: listView.verticalScrollIndicator // fixes an issue of clicks not registering right after scroll // does not give too much delay due to selection animation playing anyway diff --git a/src/EasyApp/Gui/Components/NewTableViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml similarity index 100% rename from src/EasyApp/Gui/Components/NewTableViewDelegate.qml rename to src/EasyApp/Gui/Components/ListViewDelegate.qml diff --git a/src/EasyApp/Gui/Components/qmldir b/src/EasyApp/Gui/Components/qmldir index 7437af08..d72f5978 100644 --- a/src/EasyApp/Gui/Components/qmldir +++ b/src/EasyApp/Gui/Components/qmldir @@ -21,8 +21,8 @@ SideBarColumn 1.0 SideBarColumn.qml PreferencesDialog 1.0 PreferencesDialog.qml ProjectDescriptionDialog 1.0 ProjectDescriptionDialog.qml -NewTableView 1.0 NewTableView.qml -NewTableViewDelegate 1.0 NewTableViewDelegate.qml +ListView 1.0 ListView.qml +ListViewDelegate 1.0 ListViewDelegate.qml TableView 1.0 TableView.qml TableViewHeader 1.0 TableViewHeader.qml TableViewDelegate 1.0 TableViewDelegate.qml From 6877173fd6c3763a2aa7aed6d4980181bc2ad9fb Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 27 Mar 2026 11:35:03 +0100 Subject: [PATCH 10/40] Added a flag to enable/disable multiselection --- src/EasyApp/Gui/Components/ListView.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 5ec8d709..d36b2bd6 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -28,6 +28,7 @@ Item { property ScrollBar verticalScrollBar: null property ScrollIndicator verticalScrollIndicator: null + property bool multiSelection: false // trigger for bindings property int selectionRevision: 0 @@ -65,7 +66,7 @@ Item { if (!idx) return // --- SHIFT: range selection --- - if (modifiers & Qt.ShiftModifier) { + if (listView.multiSelection && modifiers & Qt.ShiftModifier) { if (anchorRow < 0) { anchorRow = row } @@ -92,7 +93,7 @@ Item { } // --- CTRL: toggle --- - if (modifiers & Qt.ControlModifier) { + if (listView.multiSelection && modifiers & Qt.ControlModifier) { selectionModel.select( idx, ItemSelectionModel.Toggle | ItemSelectionModel.Rows From aca84182dbe4697fb3a64bfceee48e38326f42e8 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 27 Mar 2026 11:44:48 +0100 Subject: [PATCH 11/40] Added a 0.5 row length to listview Height to visually indicate that there are more items available even in abscence of scrollbar/scrollindicator --- src/EasyApp/Gui/Components/ListView.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index d36b2bd6..01dd29d8 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -12,8 +12,8 @@ Item { height: count === 0 ? 2 * EaStyle.Sizes.tableRowHeight : showHeader ? - nestedListView.tableRowHeight * (Math.min(count, maxRowCountShow) + 1 ) : - nestedListView.tableRowHeight * (Math.min(count, maxRowCountShow)) + nestedListView.tableRowHeight * (Math.min(count, maxRowCountShow + 0.5) + 1 ) : + nestedListView.tableRowHeight * (Math.min(count, maxRowCountShow + 0.5)) width: EaStyle.Sizes.sideBarContentWidth // exposing underlying tableview API @@ -28,7 +28,7 @@ Item { property ScrollBar verticalScrollBar: null property ScrollIndicator verticalScrollIndicator: null - property bool multiSelection: false + property bool multiSelection: true // trigger for bindings property int selectionRevision: 0 From a24d708b198efbad22eba717a31085ffd4ad7de0 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 27 Mar 2026 12:05:50 +0100 Subject: [PATCH 12/40] Cleaned the interface of the delegate to use listView property --- src/EasyApp/Gui/Components/ListView.qml | 2 ++ .../Gui/Components/ListViewDelegate.qml | 22 ++++++------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 01dd29d8..43127b05 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -18,6 +18,8 @@ Item { // exposing underlying tableview API property alias count: nestedListView.count + property alias currentIndex: nestedListView.currentIndex + property alias tableRowHeight: nestedListView.tableRowHeight property alias showHeader: nestedListView.showHeader property alias tallRows: nestedListView.tallRows property alias maxRowCountShow: nestedListView.maxRowCountShow diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 97acc8be..ad778c1b 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -7,16 +7,15 @@ Rectangle { id: control default property alias contentRowData: contentRow.data - //property alias mouseArea: mouseArea - property Item tableView: parent === null ? null : parent.parent + property Item listView: ListView.view.parent - implicitWidth: ListView.view.parent.width - implicitHeight: tableView === null ? EaStyle.Sizes.tableRowHeight : tableView.tableRowHeight + implicitWidth: listView.width + implicitHeight: listView.tableRowHeight color: { - ListView.view.parent.selectionRevision + listView.selectionRevision - let selected = ListView.view.parent.isSelected(index) + let selected = listView.isSelected(index) let c1 = EaStyle.Colors.themeAccentMinor let c2 = EaStyle.Colors.themeBackgroundHovered2 let c3 = EaStyle.Colors.themeBackgroundHovered1 @@ -40,17 +39,10 @@ Rectangle { cursorShape: undefined //Qt.PointingHandCursor hoverEnabled: false onReleased: (mouse) => { - control.ListView.view.parent.selectWithModifiers(index, mouse.modifiers) + listView.selectWithModifiers(index, mouse.modifiers) } } - // TapHandler { - // acceptedButtons: Qt.LeftButton // | Qt.RightButton // match whatever you need - // onTapped: (eventPoint, button) => { - // control.ListView.view.parent.selectWithModifiers(index, eventPoint.modifiers) - // } - // } - // HoverHandler to react on hover events HoverHandler { id: mouseHoverHandler @@ -60,7 +52,7 @@ Rectangle { onHoveredChanged: { if (hovered) { //console.error(`${control} [TableViewDelegate.qml] hovered`) - parent.ListView.view.currentIndex = index + listView.currentIndex = index } } } From 957059811ef5804ecbc3ab61a3c1f81463238ecb Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Thu, 9 Apr 2026 10:59:13 +0200 Subject: [PATCH 13/40] Changed the calculation of height --- src/EasyApp/Gui/Components/ListView.qml | 157 +++++++++++++++++++++--- 1 file changed, 139 insertions(+), 18 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 43127b05..84e2843d 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -9,11 +9,7 @@ import EasyApp.Gui.Components as EaComponents Item { id: listView - height: count === 0 ? - 2 * EaStyle.Sizes.tableRowHeight : - showHeader ? - nestedListView.tableRowHeight * (Math.min(count, maxRowCountShow + 0.5) + 1 ) : - nestedListView.tableRowHeight * (Math.min(count, maxRowCountShow + 0.5)) + height: nestedListView.height width: EaStyle.Sizes.sideBarContentWidth // exposing underlying tableview API @@ -28,6 +24,8 @@ Item { property alias model: nestedListView.model property alias delegate: nestedListView.delegate + property alias hasMoreRows: nestedListView.hasMoreRows + property ScrollBar verticalScrollBar: null property ScrollIndicator verticalScrollIndicator: null property bool multiSelection: true @@ -37,6 +35,9 @@ Item { // idx for shift-selection property int anchorRow: -1 + + + ItemSelectionModel { id: selectionModel model: nestedListView.model @@ -116,24 +117,144 @@ Item { selectionModel.clearSelection() } - EaComponents.TableView { + ListView { id: nestedListView + + property alias defaultInfoText: defaultInfoLabel.text + property bool showHeader: true + property bool tallRows: false + property var headerLabelItems: headerItem.children[0].children + property int contentItemChildrenLength: contentItem.children.length + property int maxRowCountShow: EaStyle.Sizes.tableMaxRowCountShow + property int tableRowHeight: tallRows ? + 1.5 * EaStyle.Sizes.tableRowHeight : + EaStyle.Sizes.tableRowHeight + + property bool hasMoreRows: count > maxRowCountShow + property real visibleRowCount: hasMoreRows ? maxRowCountShow + 0.5 : count + + enabled: count > 0 + + width: EaStyle.Sizes.sideBarContentWidth + height: count === 0 + ? 2 * EaStyle.Sizes.tableRowHeight + : showHeader + ? tableRowHeight * (visibleRowCount + 1) + : tableRowHeight * visibleRowCount + clip: true - antialiasing: true - anchors { - fill: parent - // margins: 1 - // rightMargin: 1 // scrollBar.width + headerPositioning: ListView.OverlayHeader + boundsBehavior: Flickable.StopAtBounds + + onHeaderLabelItemsChanged: setWidthOfFlexibleColumnForHeader() + onContentItemChildrenLengthChanged: widthAndAlignmentChangeTimer.start() + + // Highlight current row + highlightMoveDuration: EaStyle.Sizes.tableHighlightMoveDuration + highlight: Rectangle { + z: 2 // To display highlight rect above delegate + color: mouseHoverHandler.hovered ? + EaStyle.Colors.tableHighlight : + "transparent" + Behavior on color { EaAnimations.ThemeChange {} } + } + + // Empty header row + //header: EaComponents.TableViewHeader {} + + // Empty content rows + //delegate: EaComponents.TableViewDelegate {} + + // Table border + Rectangle { + anchors.fill: nestedListView + color: "transparent" + border.color: EaStyle.Colors.appBarComboBoxBorder + Behavior on border.color { EaAnimations.ThemeChange {} } + } + + // Default info, if no rows added + Rectangle { + visible: count === 0 + width: parent.width + height: EaStyle.Sizes.tableRowHeight * 2 + color: EaStyle.Colors.themeBackground + + Behavior on color { EaAnimations.ThemeChange {} } + + EaElements.Label { + id: defaultInfoLabel + + anchors.verticalCenter: parent.verticalCenter + leftPadding: EaStyle.Sizes.fontPixelSize + } + } + + // Width and alignment change timer + Timer { + id: widthAndAlignmentChangeTimer + interval: 10 + onTriggered: setAllColumnsWidthAndAlignment() + } + + // HoverHandler to react on hover events + // Hide current row highlight if table is not hovered + HoverHandler { + id: mouseHoverHandler + acceptedDevices: PointerDevice.AllDevices + blocking: false + onHoveredChanged: { + if (hovered) { + //console.error(`${nestedListView} [TableView.qml] hovered`) + } + } } - ScrollBar.vertical: listView.verticalScrollBar - ScrollIndicator.vertical: listView.verticalScrollIndicator + // Logic - // fixes an issue of clicks not registering right after scroll - // does not give too much delay due to selection animation playing anyway - // somehow value doesn't affect anything, just fixes the missing clicks issue - // even 10000 delay doesn't create a long delay, just fixes the issue - pressDelay: 10 + function flexibleColumnWidth() { + let fixedColumnsWidth = 0 + for (let item of headerLabelItems) { + if (!item.flexibleWidth) { + fixedColumnsWidth += item.width + } + } + const allColumnWidth = nestedListView.width + const spacingWidth = EaStyle.Sizes.tableColumnSpacing * (headerLabelItems.length - 1) + const borderThickness = EaStyle.Sizes.borderThickness * 2 + const flexibleColumnWidth = allColumnWidth - + fixedColumnsWidth - + spacingWidth - + borderThickness + return flexibleColumnWidth + } + + function setWidthOfFlexibleColumnForHeader() { + for (let item of headerLabelItems) { + if (item.flexibleWidth) { + item.width = flexibleColumnWidth() + } + } + } + + function setAllColumnsWidthAndAlignment() { + for (let item of contentItem.children) { + // Check for TableViewDelegate using explicit property + if (item.toString().startsWith('TableViewDelegate_QMLTYPE')) { + const rowElement = item.children[0] + if (rowElement && rowElement.children) { + for (let columnIndex in rowElement.children) { + if (columnIndex < headerLabelItems.length) { + rowElement.children[columnIndex].width = headerLabelItems[columnIndex].width + if (typeof rowElement.children[columnIndex].horizontalAlignment !== 'undefined') { + rowElement.children[columnIndex].horizontalAlignment = headerLabelItems[columnIndex].horizontalAlignment + } + } + } + } + } + } + } } From a8e2734eb826462e22574e66f378220c579e0ce0 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Thu, 9 Apr 2026 13:38:36 +0200 Subject: [PATCH 14/40] Changed the column width calculation Instead of dynamically catching the width of header labels and propagating them to the delegates with a timer, the widths are now defined at the top-level listview component. See the examples given --- src/EasyApp/Gui/Components/ListView.qml | 80 +++++-------------- .../Gui/Components/ListViewDelegate.qml | 17 ++++ src/EasyApp/Gui/Components/ListViewHeader.qml | 44 ++++++++++ 3 files changed, 83 insertions(+), 58 deletions(-) create mode 100644 src/EasyApp/Gui/Components/ListViewHeader.qml diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 84e2843d..7d2cf2bb 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -26,6 +26,25 @@ Item { property alias hasMoreRows: nestedListView.hasMoreRows + // Column layout definition. Each entry: { width: , alignment: } + // Use width: -1 for a column that fills remaining space. Example: + // columns: [ + // { width: 40, alignment: Text.AlignHCenter }, + // { width: -1, alignment: Text.AlignLeft }, + // { width: 100, alignment: Text.AlignRight } + // ] + property var columns: [] + readonly property var resolvedColumnWidths: { + if (!columns.length) return [] + let fixed = 0, flexCount = 0 + for (let c of columns) + c.width > 0 ? fixed += c.width : flexCount++ + const spacing = EaStyle.Sizes.tableColumnSpacing * (columns.length - 1) + const border = EaStyle.Sizes.borderThickness * 2 + const fill = flexCount > 0 ? (width - fixed - spacing - border) / flexCount : 0 + return columns.map(c => c.width > 0 ? c.width : fill) + } + property ScrollBar verticalScrollBar: null property ScrollIndicator verticalScrollIndicator: null property bool multiSelection: true @@ -48,6 +67,8 @@ Item { function onSelectionChanged() { listView.selectionRevision++ + if (selectionModel.selectedIndexes.length === 0) + anchorRow = -1 } } @@ -115,6 +136,7 @@ Item { function clearSelection() { selectionModel.clearSelection() + anchorRow = -1 } ListView { @@ -123,8 +145,6 @@ Item { property alias defaultInfoText: defaultInfoLabel.text property bool showHeader: true property bool tallRows: false - property var headerLabelItems: headerItem.children[0].children - property int contentItemChildrenLength: contentItem.children.length property int maxRowCountShow: EaStyle.Sizes.tableMaxRowCountShow property int tableRowHeight: tallRows ? 1.5 * EaStyle.Sizes.tableRowHeight : @@ -146,9 +166,6 @@ Item { headerPositioning: ListView.OverlayHeader boundsBehavior: Flickable.StopAtBounds - onHeaderLabelItemsChanged: setWidthOfFlexibleColumnForHeader() - onContentItemChildrenLengthChanged: widthAndAlignmentChangeTimer.start() - // Highlight current row highlightMoveDuration: EaStyle.Sizes.tableHighlightMoveDuration highlight: Rectangle { @@ -190,13 +207,6 @@ Item { } } - // Width and alignment change timer - Timer { - id: widthAndAlignmentChangeTimer - interval: 10 - onTriggered: setAllColumnsWidthAndAlignment() - } - // HoverHandler to react on hover events // Hide current row highlight if table is not hovered HoverHandler { @@ -210,52 +220,6 @@ Item { } } - // Logic - - function flexibleColumnWidth() { - let fixedColumnsWidth = 0 - for (let item of headerLabelItems) { - if (!item.flexibleWidth) { - fixedColumnsWidth += item.width - } - } - const allColumnWidth = nestedListView.width - const spacingWidth = EaStyle.Sizes.tableColumnSpacing * (headerLabelItems.length - 1) - const borderThickness = EaStyle.Sizes.borderThickness * 2 - const flexibleColumnWidth = allColumnWidth - - fixedColumnsWidth - - spacingWidth - - borderThickness - return flexibleColumnWidth - } - - function setWidthOfFlexibleColumnForHeader() { - for (let item of headerLabelItems) { - if (item.flexibleWidth) { - item.width = flexibleColumnWidth() - } - } - } - - function setAllColumnsWidthAndAlignment() { - for (let item of contentItem.children) { - // Check for TableViewDelegate using explicit property - if (item.toString().startsWith('TableViewDelegate_QMLTYPE')) { - const rowElement = item.children[0] - if (rowElement && rowElement.children) { - for (let columnIndex in rowElement.children) { - if (columnIndex < headerLabelItems.length) { - rowElement.children[columnIndex].width = headerLabelItems[columnIndex].width - if (typeof rowElement.children[columnIndex].horizontalAlignment !== 'undefined') { - rowElement.children[columnIndex].horizontalAlignment = headerLabelItems[columnIndex].horizontalAlignment - } - } - } - } - } - } - } - } } diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index ad778c1b..19146397 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -24,6 +24,23 @@ Rectangle { } Behavior on color { EaAnimations.ThemeChange {} } + function syncColumnWidths() { + let widths = listView.resolvedColumnWidths + let cols = listView.columns + for (let i = 0; i < contentRow.children.length && i < widths.length; i++) { + contentRow.children[i].width = widths[i] + if (cols[i] && typeof contentRow.children[i].horizontalAlignment !== 'undefined') + contentRow.children[i].horizontalAlignment = cols[i].alignment + } + } + + Component.onCompleted: syncColumnWidths() + + Connections { + target: listView + function onResolvedColumnWidthsChanged() { syncColumnWidths() } + } + Row { id: contentRow diff --git a/src/EasyApp/Gui/Components/ListViewHeader.qml b/src/EasyApp/Gui/Components/ListViewHeader.qml new file mode 100644 index 00000000..16da992b --- /dev/null +++ b/src/EasyApp/Gui/Components/ListViewHeader.qml @@ -0,0 +1,44 @@ +import QtQuick +import QtQuick.Controls + +import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Animations as EaAnimations + +Rectangle { + default property alias contentRowData: contentRow.data + property Item listView: parent.parent + + visible: listView.showHeader + + z: 3 // To display header above delegate and highlighted area + + implicitWidth: parent === null ? 0 : parent.width + implicitHeight: listView.showHeader ? listView.tableRowHeight : 0 + + color: EaStyle.Colors.contentBackground + Behavior on color { EaAnimations.ThemeChange {} } + + function syncColumnWidths() { + let widths = listView.resolvedColumnWidths + let cols = listView.columns + for (let i = 0; i < contentRow.children.length && i < widths.length; i++) { + contentRow.children[i].width = widths[i] + if (cols[i] && typeof contentRow.children[i].horizontalAlignment !== 'undefined') + contentRow.children[i].horizontalAlignment = cols[i].alignment + } + } + + Component.onCompleted: syncColumnWidths() + + Connections { + target: listView + function onResolvedColumnWidthsChanged() { syncColumnWidths() } + } + + Row { + id: contentRow + + height: parent.height + spacing: EaStyle.Sizes.tableColumnSpacing + } +} From 0b24b4d5773db68f7efc4ab5c211b67b52d2f7b8 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Thu, 9 Apr 2026 13:39:45 +0200 Subject: [PATCH 15/40] added ListViewHeader to qmldir for imports --- src/EasyApp/Gui/Components/qmldir | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EasyApp/Gui/Components/qmldir b/src/EasyApp/Gui/Components/qmldir index d72f5978..576f2a8f 100644 --- a/src/EasyApp/Gui/Components/qmldir +++ b/src/EasyApp/Gui/Components/qmldir @@ -23,6 +23,7 @@ ProjectDescriptionDialog 1.0 ProjectDescriptionDialog.qml ListView 1.0 ListView.qml ListViewDelegate 1.0 ListViewDelegate.qml +ListViewHeader 1.0 ListViewHeader.qml TableView 1.0 TableView.qml TableViewHeader 1.0 TableViewHeader.qml TableViewDelegate 1.0 TableViewDelegate.qml From 963648d8b2216bb5615ff674e620adc68d6e6fc5 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Thu, 9 Apr 2026 14:19:08 +0200 Subject: [PATCH 16/40] listview cleanup --- src/EasyApp/Gui/Components/ListView.qml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 7d2cf2bb..99f90023 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -47,6 +47,8 @@ Item { property ScrollBar verticalScrollBar: null property ScrollIndicator verticalScrollIndicator: null + + // flag to limit selections property bool multiSelection: true // trigger for bindings @@ -54,9 +56,6 @@ Item { // idx for shift-selection property int anchorRow: -1 - - - ItemSelectionModel { id: selectionModel model: nestedListView.model @@ -176,12 +175,6 @@ Item { Behavior on color { EaAnimations.ThemeChange {} } } - // Empty header row - //header: EaComponents.TableViewHeader {} - - // Empty content rows - //delegate: EaComponents.TableViewDelegate {} - // Table border Rectangle { anchors.fill: nestedListView @@ -213,13 +206,7 @@ Item { id: mouseHoverHandler acceptedDevices: PointerDevice.AllDevices blocking: false - onHoveredChanged: { - if (hovered) { - //console.error(`${nestedListView} [TableView.qml] hovered`) - } } } - } - } From 0c24d0ff96ac37d008ab645c91c74e8ec7d0ca71 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Thu, 9 Apr 2026 17:22:58 +0200 Subject: [PATCH 17/40] Fixing vibecoding issues --- src/EasyApp/Gui/Components/ListView.qml | 57 +++++++++++-------- .../Gui/Components/ListViewDelegate.qml | 24 ++------ src/EasyApp/Gui/Components/ListViewHeader.qml | 21 ++----- 3 files changed, 44 insertions(+), 58 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 99f90023..e09828d0 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -21,28 +21,32 @@ Item { property alias maxRowCountShow: nestedListView.maxRowCountShow property alias defaultInfoText: nestedListView.defaultInfoText property alias header: nestedListView.header + // Must be a QAbstractItemModel (e.g. ListModel). JS arrays or integers + // won't support selection. property alias model: nestedListView.model property alias delegate: nestedListView.delegate property alias hasMoreRows: nestedListView.hasMoreRows - // Column layout definition. Each entry: { width: , alignment: } - // Use width: -1 for a column that fills remaining space. Example: - // columns: [ - // { width: 40, alignment: Text.AlignHCenter }, - // { width: -1, alignment: Text.AlignLeft }, - // { width: 100, alignment: Text.AlignRight } - // ] - property var columns: [] + // Column widths definition. Each entry is a width in px, or -1 to fill remaining space. Example: + // columnWidths: [40, -1, 100] + property var columnWidths: [] readonly property var resolvedColumnWidths: { - if (!columns.length) return [] + if (!columnWidths.length) return [] let fixed = 0, flexCount = 0 - for (let c of columns) - c.width > 0 ? fixed += c.width : flexCount++ - const spacing = EaStyle.Sizes.tableColumnSpacing * (columns.length - 1) + for (let w of columnWidths) + w > 0 ? fixed += w : flexCount++ + const spacing = EaStyle.Sizes.tableColumnSpacing * (columnWidths.length - 1) const border = EaStyle.Sizes.borderThickness * 2 - const fill = flexCount > 0 ? (width - fixed - spacing - border) / flexCount : 0 - return columns.map(c => c.width > 0 ? c.width : fill) + // Remaining space after fixed columns, inter-column spacing, and border, + // divided equally among flex columns (width: -1). Clamped to 0. + const fill = flexCount > 0 ? Math.max(0, (width - fixed - spacing - border) / flexCount) : 0 + return columnWidths.map(w => w > 0 ? w : fill) + } + + function applyWidths(row) { + for (let i = 0; i < row.children.length && i < resolvedColumnWidths.length; i++) + row.children[i].width = resolvedColumnWidths[i] } property ScrollBar verticalScrollBar: null @@ -94,6 +98,7 @@ Item { anchorRow = row } + let savedAnchor = anchorRow let from = Math.min(anchorRow, row) let to = Math.max(anchorRow, row) @@ -112,6 +117,7 @@ Item { } } + anchorRow = savedAnchor return } @@ -153,8 +159,10 @@ Item { property real visibleRowCount: hasMoreRows ? maxRowCountShow + 0.5 : count enabled: count > 0 + // fixes an issue of clicks not registering right after scroll + pressDelay: 10 - width: EaStyle.Sizes.sideBarContentWidth + width: parent.width height: count === 0 ? 2 * EaStyle.Sizes.tableRowHeight : showHeader @@ -175,14 +183,6 @@ Item { Behavior on color { EaAnimations.ThemeChange {} } } - // Table border - Rectangle { - anchors.fill: nestedListView - color: "transparent" - border.color: EaStyle.Colors.appBarComboBoxBorder - Behavior on border.color { EaAnimations.ThemeChange {} } - } - // Default info, if no rows added Rectangle { visible: count === 0 @@ -200,13 +200,20 @@ Item { } } - // HoverHandler to react on hover events // Hide current row highlight if table is not hovered HoverHandler { id: mouseHoverHandler acceptedDevices: PointerDevice.AllDevices blocking: false - } } } + + // Table border, z above all content (header z:3, highlight z:2) + Rectangle { + z: 4 + anchors.fill: parent + color: "transparent" + border.color: EaStyle.Colors.appBarComboBoxBorder + Behavior on border.color { EaAnimations.ThemeChange {} } + } } diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 19146397..c8f83376 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -24,21 +24,11 @@ Rectangle { } Behavior on color { EaAnimations.ThemeChange {} } - function syncColumnWidths() { - let widths = listView.resolvedColumnWidths - let cols = listView.columns - for (let i = 0; i < contentRow.children.length && i < widths.length; i++) { - contentRow.children[i].width = widths[i] - if (cols[i] && typeof contentRow.children[i].horizontalAlignment !== 'undefined') - contentRow.children[i].horizontalAlignment = cols[i].alignment - } - } - - Component.onCompleted: syncColumnWidths() + Component.onCompleted: if (listView) listView.applyWidths(contentRow) Connections { target: listView - function onResolvedColumnWidthsChanged() { syncColumnWidths() } + function onResolvedColumnWidthsChanged() { listView.applyWidths(contentRow) } } Row { @@ -53,24 +43,22 @@ Rectangle { id: mouseArea anchors.fill: parent propagateComposedEvents: true - cursorShape: undefined //Qt.PointingHandCursor hoverEnabled: false - onReleased: (mouse) => { + onClicked: (mouse) => { listView.selectWithModifiers(index, mouse.modifiers) } } - // HoverHandler to react on hover events + // Mouse-only navigation: highlight follows hover. + // If keyboard navigation is added, guard this with a keyboardActive flag. HoverHandler { id: mouseHoverHandler acceptedDevices: PointerDevice.AllDevices cursorShape: Qt.PointingHandCursor blocking: false onHoveredChanged: { - if (hovered) { - //console.error(`${control} [TableViewDelegate.qml] hovered`) + if (hovered) listView.currentIndex = index - } } } } diff --git a/src/EasyApp/Gui/Components/ListViewHeader.qml b/src/EasyApp/Gui/Components/ListViewHeader.qml index 16da992b..3e34ae71 100644 --- a/src/EasyApp/Gui/Components/ListViewHeader.qml +++ b/src/EasyApp/Gui/Components/ListViewHeader.qml @@ -5,34 +5,25 @@ import EasyApp.Gui.Style as EaStyle import EasyApp.Gui.Animations as EaAnimations Rectangle { + id: listViewHeader default property alias contentRowData: contentRow.data - property Item listView: parent.parent + property Item listView: ListView.view ? ListView.view.parent : null - visible: listView.showHeader + visible: listView && listView.showHeader z: 3 // To display header above delegate and highlighted area implicitWidth: parent === null ? 0 : parent.width - implicitHeight: listView.showHeader ? listView.tableRowHeight : 0 + implicitHeight: listView && listView.showHeader ? listView.tableRowHeight : 0 color: EaStyle.Colors.contentBackground Behavior on color { EaAnimations.ThemeChange {} } - function syncColumnWidths() { - let widths = listView.resolvedColumnWidths - let cols = listView.columns - for (let i = 0; i < contentRow.children.length && i < widths.length; i++) { - contentRow.children[i].width = widths[i] - if (cols[i] && typeof contentRow.children[i].horizontalAlignment !== 'undefined') - contentRow.children[i].horizontalAlignment = cols[i].alignment - } - } - - Component.onCompleted: syncColumnWidths() + Component.onCompleted: Qt.callLater(function() { if (listView) listView.applyWidths(contentRow) }) Connections { target: listView - function onResolvedColumnWidthsChanged() { syncColumnWidths() } + function onResolvedColumnWidthsChanged() { listView.applyWidths(contentRow) } } Row { From bbbedfdfb546fa54598dd514463c0c9e4074d3d5 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 10 Apr 2026 17:46:15 +0200 Subject: [PATCH 18/40] Refactored listview to not be encapsulated in item, resolved minor issues --- src/EasyApp/Gui/Components/ListView.qml | 225 +++++++++--------- .../Gui/Components/ListViewDelegate.qml | 14 +- src/EasyApp/Gui/Components/ListViewHeader.qml | 6 +- 3 files changed, 125 insertions(+), 120 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index e09828d0..b76a2685 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -1,5 +1,5 @@ import QtQuick -import QtQuick.Controls +import QtQuick.Controls as QC import EasyApp.Gui.Globals as EaGlobals import EasyApp.Gui.Style as EaStyle @@ -7,35 +7,43 @@ import EasyApp.Gui.Animations as EaAnimations import EasyApp.Gui.Elements as EaElements import EasyApp.Gui.Components as EaComponents -Item { +QC.ListView { id: listView - height: nestedListView.height width: EaStyle.Sizes.sideBarContentWidth - // exposing underlying tableview API - property alias count: nestedListView.count - property alias currentIndex: nestedListView.currentIndex - property alias tableRowHeight: nestedListView.tableRowHeight - property alias showHeader: nestedListView.showHeader - property alias tallRows: nestedListView.tallRows - property alias maxRowCountShow: nestedListView.maxRowCountShow - property alias defaultInfoText: nestedListView.defaultInfoText - property alias header: nestedListView.header - // Must be a QAbstractItemModel (e.g. ListModel). JS arrays or integers - // won't support selection. - property alias model: nestedListView.model - property alias delegate: nestedListView.delegate - - property alias hasMoreRows: nestedListView.hasMoreRows - - // Column widths definition. Each entry is a width in px, or -1 to fill remaining space. Example: + property bool showHeader: true + property bool tallRows: false + property int maxRowCountShow: EaStyle.Sizes.tableMaxRowCountShow + property int tableRowHeight: tallRows ? + 1.5 * EaStyle.Sizes.tableRowHeight : + EaStyle.Sizes.tableRowHeight + property alias defaultInfoText: defaultInfoLabel.text + + + enum ScrollBarMode { + Indicator, + AsNeeded, + AlwaysOn + } + property int scrollBarMode: ListView.Indicator + + // flag to limit selections + property bool multiSelection: true + + // used to bind delegate colors to current selections + readonly property var selectedIndexes: selectionModel.selectedIndexes + + // Column widths definition. Each entry is a width in px, or -1 to fill remaining space. + // Example: // columnWidths: [40, -1, 100] property var columnWidths: [] readonly property var resolvedColumnWidths: { if (!columnWidths.length) return [] let fixed = 0, flexCount = 0 - for (let w of columnWidths) - w > 0 ? fixed += w : flexCount++ + for (let w of columnWidths) { + if (w > 0) fixed += w + else flexCount++ + } const spacing = EaStyle.Sizes.tableColumnSpacing * (columnWidths.length - 1) const border = EaStyle.Sizes.borderThickness * 2 // Remaining space after fixed columns, inter-column spacing, and border, @@ -44,40 +52,108 @@ Item { return columnWidths.map(w => w > 0 ? w : fill) } - function applyWidths(row) { - for (let i = 0; i < row.children.length && i < resolvedColumnWidths.length; i++) - row.children[i].width = resolvedColumnWidths[i] + // idx for shift-selection + property int anchorRow: -1 + + + // fixes an issue of clicks not registering right after scroll + pressDelay: 10 + + property bool hasMoreRows: count > maxRowCountShow + property real visibleRowCount: hasMoreRows ? maxRowCountShow + 0.5 : count + height: count === 0 + ? 2 * EaStyle.Sizes.tableRowHeight + : showHeader + ? tableRowHeight * (visibleRowCount + 1) + : tableRowHeight * visibleRowCount + + clip: true + headerPositioning: QC.ListView.OverlayHeader + boundsBehavior: Flickable.StopAtBounds + enabled: count > 0 + // Highlight current row + highlightMoveDuration: EaStyle.Sizes.tableHighlightMoveDuration + highlight: Rectangle { + z: 2 // To display highlight rect above delegate + color: mouseHoverHandler.hovered ? + EaStyle.Colors.tableHighlight : + "transparent" + Behavior on color { EaAnimations.ThemeChange {} } } - property ScrollBar verticalScrollBar: null - property ScrollIndicator verticalScrollIndicator: null + // Hide current row highlight if table is not hovered + HoverHandler { + id: mouseHoverHandler + acceptedDevices: PointerDevice.AllDevices + blocking: false + } - // flag to limit selections - property bool multiSelection: true + QC.ScrollBar.vertical: EaElements.ScrollBar { + policy: scrollBarMode === ListView.AlwaysOn ? QC.ScrollBar.AlwaysOn + : scrollBarMode === ListView.AsNeeded ? QC.ScrollBar.AsNeeded + : QC.ScrollBar.AlwaysOff + topInset: listView.showHeader ? listView.tableRowHeight : 0 + topPadding: listView.padding + (listView.showHeader ? listView.tableRowHeight : 0) + } - // trigger for bindings - property int selectionRevision: 0 - // idx for shift-selection - property int anchorRow: -1 + QC.ScrollIndicator.vertical: EaElements.ScrollIndicator { + active: scrollBarMode === ListView.Indicator + topInset: listView.showHeader ? listView.tableRowHeight : 0 + topPadding: listView.padding + (listView.showHeader ? listView.tableRowHeight : 0) + } + + // Default info, if no rows added. + // Parented directly to listView so it doesn't scroll with content. + Rectangle { + parent: listView + visible: listView.count === 0 + width: listView.width + height: EaStyle.Sizes.tableRowHeight * 2 + color: EaStyle.Colors.themeBackground + + Behavior on color { EaAnimations.ThemeChange {} } + + EaElements.Label { + id: defaultInfoLabel + + anchors.verticalCenter: parent.verticalCenter + leftPadding: EaStyle.Sizes.fontPixelSize + } + } + + // Table border, z above all content (header z:3, highlight z:2). + // Parented directly to listView so it doesn't scroll with content. + Rectangle { + parent: listView + z: 4 + anchors.fill: parent + color: "transparent" + border.color: EaStyle.Colors.appBarComboBoxBorder + Behavior on border.color { EaAnimations.ThemeChange {} } + } ItemSelectionModel { id: selectionModel - model: nestedListView.model + model: listView.model } Connections { target: selectionModel function onSelectionChanged() { - listView.selectionRevision++ if (selectionModel.selectedIndexes.length === 0) anchorRow = -1 } } + function applyWidths(row) { + for (let i = 0; i < row.children.length && i < resolvedColumnWidths.length; i++) + row.children[i].width = resolvedColumnWidths[i] + } + // --- helper: convert row -> QModelIndex --- function _index(row) { - if (!selectionModel.model) + if (!selectionModel.model || row < 0 || row >= count) return null return selectionModel.model.index(row, 0) } @@ -85,7 +161,7 @@ Item { // --- public API --- function isSelected(row) { let idx = _index(row) - return idx ? selectionModel.isSelected(idx) : false + return idx && idx.valid ? selectionModel.isSelected(idx) : false } function selectWithModifiers(row, modifiers) { @@ -102,7 +178,7 @@ Item { let from = Math.min(anchorRow, row) let to = Math.max(anchorRow, row) - // If Ctrl is NOT pressed → replace selection + // If Ctrl is NOT pressed -> replace selection if (!(modifiers & Qt.ControlModifier)) { selectionModel.clearSelection() } @@ -143,77 +219,4 @@ Item { selectionModel.clearSelection() anchorRow = -1 } - - ListView { - id: nestedListView - - property alias defaultInfoText: defaultInfoLabel.text - property bool showHeader: true - property bool tallRows: false - property int maxRowCountShow: EaStyle.Sizes.tableMaxRowCountShow - property int tableRowHeight: tallRows ? - 1.5 * EaStyle.Sizes.tableRowHeight : - EaStyle.Sizes.tableRowHeight - - property bool hasMoreRows: count > maxRowCountShow - property real visibleRowCount: hasMoreRows ? maxRowCountShow + 0.5 : count - - enabled: count > 0 - // fixes an issue of clicks not registering right after scroll - pressDelay: 10 - - width: parent.width - height: count === 0 - ? 2 * EaStyle.Sizes.tableRowHeight - : showHeader - ? tableRowHeight * (visibleRowCount + 1) - : tableRowHeight * visibleRowCount - - clip: true - headerPositioning: ListView.OverlayHeader - boundsBehavior: Flickable.StopAtBounds - - // Highlight current row - highlightMoveDuration: EaStyle.Sizes.tableHighlightMoveDuration - highlight: Rectangle { - z: 2 // To display highlight rect above delegate - color: mouseHoverHandler.hovered ? - EaStyle.Colors.tableHighlight : - "transparent" - Behavior on color { EaAnimations.ThemeChange {} } - } - - // Default info, if no rows added - Rectangle { - visible: count === 0 - width: parent.width - height: EaStyle.Sizes.tableRowHeight * 2 - color: EaStyle.Colors.themeBackground - - Behavior on color { EaAnimations.ThemeChange {} } - - EaElements.Label { - id: defaultInfoLabel - - anchors.verticalCenter: parent.verticalCenter - leftPadding: EaStyle.Sizes.fontPixelSize - } - } - - // Hide current row highlight if table is not hovered - HoverHandler { - id: mouseHoverHandler - acceptedDevices: PointerDevice.AllDevices - blocking: false - } - } - - // Table border, z above all content (header z:3, highlight z:2) - Rectangle { - z: 4 - anchors.fill: parent - color: "transparent" - border.color: EaStyle.Colors.appBarComboBoxBorder - Behavior on border.color { EaAnimations.ThemeChange {} } - } } diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index c8f83376..e8959b37 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -7,15 +7,17 @@ Rectangle { id: control default property alias contentRowData: contentRow.data - property Item listView: ListView.view.parent + property Item listView: ListView.view ?? null implicitWidth: listView.width implicitHeight: listView.tableRowHeight color: { - listView.selectionRevision + // Read selectedIndexes to create a binding dependency — forces + // this color expression to re-evaluate whenever the selection changes. + listView.selectedIndexes - let selected = listView.isSelected(index) + let selected = index >= 0 && listView.isSelected(index) let c1 = EaStyle.Colors.themeAccentMinor let c2 = EaStyle.Colors.themeBackgroundHovered2 let c3 = EaStyle.Colors.themeBackgroundHovered1 @@ -42,10 +44,10 @@ Rectangle { MouseArea { id: mouseArea anchors.fill: parent - propagateComposedEvents: true hoverEnabled: false onClicked: (mouse) => { - listView.selectWithModifiers(index, mouse.modifiers) + if (index >= 0) + listView.selectWithModifiers(index, mouse.modifiers) } } @@ -57,7 +59,7 @@ Rectangle { cursorShape: Qt.PointingHandCursor blocking: false onHoveredChanged: { - if (hovered) + if (hovered && index >= 0) listView.currentIndex = index } } diff --git a/src/EasyApp/Gui/Components/ListViewHeader.qml b/src/EasyApp/Gui/Components/ListViewHeader.qml index 3e34ae71..4636fa8c 100644 --- a/src/EasyApp/Gui/Components/ListViewHeader.qml +++ b/src/EasyApp/Gui/Components/ListViewHeader.qml @@ -1,5 +1,5 @@ import QtQuick -import QtQuick.Controls +import QtQuick.Controls as QC import EasyApp.Gui.Style as EaStyle import EasyApp.Gui.Animations as EaAnimations @@ -7,7 +7,7 @@ import EasyApp.Gui.Animations as EaAnimations Rectangle { id: listViewHeader default property alias contentRowData: contentRow.data - property Item listView: ListView.view ? ListView.view.parent : null + property Item listView: ListView.view ?? null visible: listView && listView.showHeader @@ -19,7 +19,7 @@ Rectangle { color: EaStyle.Colors.contentBackground Behavior on color { EaAnimations.ThemeChange {} } - Component.onCompleted: Qt.callLater(function() { if (listView) listView.applyWidths(contentRow) }) + Component.onCompleted: if (listView) listView.applyWidths(contentRow) Connections { target: listView From 7ddaa63b01ba11620990f70bbc18d108bb3d2137 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 10 Apr 2026 18:16:35 +0200 Subject: [PATCH 19/40] Namspace clash fix --- src/EasyApp/Gui/Components/ListView.qml | 23 +++++++++++-------- src/EasyApp/Gui/Components/ListViewHeader.qml | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index b76a2685..f34498de 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -1,5 +1,5 @@ import QtQuick -import QtQuick.Controls as QC +import QtQuick.Controls import EasyApp.Gui.Globals as EaGlobals import EasyApp.Gui.Style as EaStyle @@ -7,7 +7,7 @@ import EasyApp.Gui.Animations as EaAnimations import EasyApp.Gui.Elements as EaElements import EasyApp.Gui.Components as EaComponents -QC.ListView { +ListView { id: listView width: EaStyle.Sizes.sideBarContentWidth @@ -20,12 +20,15 @@ QC.ListView { property alias defaultInfoText: defaultInfoLabel.text + // Externally, use the full module path: EaComponents.ListView.Indicator, etc. + // Unqualified "ListView.Indicator" will resolve to QtQuick's ListView, not this component. + // Internally, we reference via the id (listView.Indicator) to avoid the same clash. enum ScrollBarMode { Indicator, AsNeeded, AlwaysOn } - property int scrollBarMode: ListView.Indicator + property int scrollBarMode: listView.Indicator // flag to limit selections property bool multiSelection: true @@ -68,7 +71,7 @@ QC.ListView { : tableRowHeight * visibleRowCount clip: true - headerPositioning: QC.ListView.OverlayHeader + headerPositioning: ListView.OverlayHeader boundsBehavior: Flickable.StopAtBounds enabled: count > 0 // Highlight current row @@ -88,16 +91,16 @@ QC.ListView { blocking: false } - QC.ScrollBar.vertical: EaElements.ScrollBar { - policy: scrollBarMode === ListView.AlwaysOn ? QC.ScrollBar.AlwaysOn - : scrollBarMode === ListView.AsNeeded ? QC.ScrollBar.AsNeeded - : QC.ScrollBar.AlwaysOff + ScrollBar.vertical: EaElements.ScrollBar { + policy: scrollBarMode === listView.AlwaysOn ? ScrollBar.AlwaysOn + : scrollBarMode === listView.AsNeeded ? ScrollBar.AsNeeded + : ScrollBar.AlwaysOff topInset: listView.showHeader ? listView.tableRowHeight : 0 topPadding: listView.padding + (listView.showHeader ? listView.tableRowHeight : 0) } - QC.ScrollIndicator.vertical: EaElements.ScrollIndicator { - active: scrollBarMode === ListView.Indicator + ScrollIndicator.vertical: EaElements.ScrollIndicator { + active: scrollBarMode === listView.Indicator topInset: listView.showHeader ? listView.tableRowHeight : 0 topPadding: listView.padding + (listView.showHeader ? listView.tableRowHeight : 0) } diff --git a/src/EasyApp/Gui/Components/ListViewHeader.qml b/src/EasyApp/Gui/Components/ListViewHeader.qml index 4636fa8c..a28463b4 100644 --- a/src/EasyApp/Gui/Components/ListViewHeader.qml +++ b/src/EasyApp/Gui/Components/ListViewHeader.qml @@ -1,5 +1,5 @@ import QtQuick -import QtQuick.Controls as QC +import QtQuick.Controls import EasyApp.Gui.Style as EaStyle import EasyApp.Gui.Animations as EaAnimations From a98f69c430f883e51189df3299db5537a87eddf5 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 13 Apr 2026 11:40:46 +0200 Subject: [PATCH 20/40] Refactor ListView: separate public/companion/internal API, derive header from headerItem --- src/EasyApp/Gui/Components/ListView.qml | 243 ++++++++++-------- src/EasyApp/Gui/Components/ListViewHeader.qml | 4 +- 2 files changed, 130 insertions(+), 117 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index f34498de..4b93ae70 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -9,20 +9,25 @@ import EasyApp.Gui.Components as EaComponents ListView { id: listView + + // ── Public API ────────────────────────────────────────────────────── + // Properties and functions for consumers instantiating this component. + width: EaStyle.Sizes.sideBarContentWidth - property bool showHeader: true + // When true, rows use 1.5x height. property bool tallRows: false + + // Max visible rows before scrolling kicks in. property int maxRowCountShow: EaStyle.Sizes.tableMaxRowCountShow - property int tableRowHeight: tallRows ? - 1.5 * EaStyle.Sizes.tableRowHeight : - EaStyle.Sizes.tableRowHeight + + // Text shown when ListView model is empty. property alias defaultInfoText: defaultInfoLabel.text - - // Externally, use the full module path: EaComponents.ListView.Indicator, etc. - // Unqualified "ListView.Indicator" will resolve to QtQuick's ListView, not this component. - // Internally, we reference via the id (listView.Indicator) to avoid the same clash. + // Scroll bar display mode. + // Externally, use full module path: EaComponents.ListView.Indicator, etc. + // Internally, use id-based path: listView.Indicator, etc. + // Unqualified "ListView.Indicator" resolves to QtQuick's ListView, not this component. enum ScrollBarMode { Indicator, AsNeeded, @@ -30,16 +35,34 @@ ListView { } property int scrollBarMode: listView.Indicator - // flag to limit selections + // Allow ctrl/shift multi-select. property bool multiSelection: true - // used to bind delegate colors to current selections - readonly property var selectedIndexes: selectionModel.selectedIndexes - // Column widths definition. Each entry is a width in px, or -1 to fill remaining space. - // Example: - // columnWidths: [40, -1, 100] + // Example: columnWidths: [40, -1, 100] property var columnWidths: [] + + // Clear all selection and reset anchor. + function clearSelection() { + selectionModel.clearSelection() + anchorRow = -1 + } + + // ── Companion API ─────────────────────────────────────────────────── + // Used by ListViewHeader and ListViewDelegate. Not intended for direct consumer use. + + // Row height in px, derived from tallRows. + // Used by: ListViewDelegate (implicitHeight), ListViewHeader (own height) + property int tableRowHeight: tallRows ? + 1.5 * EaStyle.Sizes.tableRowHeight : + EaStyle.Sizes.tableRowHeight + + // Current selection state. + // Used by: ListViewDelegate (binding dependency for row color) + readonly property var selectedIndexes: selectionModel.selectedIndexes + + // Computed px widths from columnWidths. + // Used by: ListViewHeader + ListViewDelegate (subscribe via onResolvedColumnWidthsChanged) readonly property var resolvedColumnWidths: { if (!columnWidths.length) return [] let fixed = 0, flexCount = 0 @@ -49,42 +72,114 @@ ListView { } const spacing = EaStyle.Sizes.tableColumnSpacing * (columnWidths.length - 1) const border = EaStyle.Sizes.borderThickness * 2 - // Remaining space after fixed columns, inter-column spacing, and border, - // divided equally among flex columns (width: -1). Clamped to 0. const fill = flexCount > 0 ? Math.max(0, (width - fixed - spacing - border) / flexCount) : 0 return columnWidths.map(w => w > 0 ? w : fill) } - // idx for shift-selection + // Apply resolvedColumnWidths to children of a Row item. + // Used by: ListViewHeader + ListViewDelegate (onCompleted + onResolvedColumnWidthsChanged) + function applyWidths(row) { + for (let i = 0; i < row.children.length && i < resolvedColumnWidths.length; i++) + row.children[i].width = resolvedColumnWidths[i] + } + + // Check if given row index is selected. + // Used by: ListViewDelegate (row background color) + function isSelected(row) { + let idx = _index(row) + return idx && idx.valid ? selectionModel.isSelected(idx) : false + } + + // Select row with ctrl/shift modifier logic. + // Used by: ListViewDelegate (MouseArea.onClicked) + function selectWithModifiers(row, modifiers) { + let idx = _index(row) + if (!idx) return + + // SHIFT: range selection + if (listView.multiSelection && modifiers & Qt.ShiftModifier) { + if (anchorRow < 0) { + anchorRow = row + } + + let savedAnchor = anchorRow + let from = Math.min(anchorRow, row) + let to = Math.max(anchorRow, row) + + if (!(modifiers & Qt.ControlModifier)) { + selectionModel.clearSelection() + } + + for (let i = from; i <= to; i++) { + let rIdx = _index(i) + if (rIdx) { + selectionModel.select( + rIdx, + ItemSelectionModel.Select | ItemSelectionModel.Rows + ) + } + } + + anchorRow = savedAnchor + return + } + + // CTRL: toggle + if (listView.multiSelection && modifiers & Qt.ControlModifier) { + selectionModel.select( + idx, + ItemSelectionModel.Toggle | ItemSelectionModel.Rows + ) + anchorRow = row + return + } + + // DEFAULT: single selection + selectionModel.select( + idx, + ItemSelectionModel.ClearAndSelect | ItemSelectionModel.Rows + ) + anchorRow = row + } + + // ── Internals ─────────────────────────────────────────────────────── + + // Anchor row index for shift-selection range tracking. property int anchorRow: -1 - - // fixes an issue of clicks not registering right after scroll + // Convert row int to QModelIndex for selectionModel. + function _index(row) { + if (!selectionModel.model || row < 0 || row >= count) + return null + return selectionModel.model.index(row, 0) + } + + // Fixes clicks not registering right after scroll. pressDelay: 10 property bool hasMoreRows: count > maxRowCountShow property real visibleRowCount: hasMoreRows ? maxRowCountShow + 0.5 : count + // headerItem is non-null when a header delegate is set (e.g. ListViewHeader). + // Uses actual headerItem.height so custom headers with different heights work. + property real _headerHeight: headerItem ? headerItem.height : 0 height: count === 0 ? 2 * EaStyle.Sizes.tableRowHeight - : showHeader - ? tableRowHeight * (visibleRowCount + 1) - : tableRowHeight * visibleRowCount + : tableRowHeight * visibleRowCount + _headerHeight clip: true headerPositioning: ListView.OverlayHeader boundsBehavior: Flickable.StopAtBounds enabled: count > 0 - // Highlight current row + highlightMoveDuration: EaStyle.Sizes.tableHighlightMoveDuration highlight: Rectangle { - z: 2 // To display highlight rect above delegate + z: 2 color: mouseHoverHandler.hovered ? EaStyle.Colors.tableHighlight : "transparent" Behavior on color { EaAnimations.ThemeChange {} } } - // Hide current row highlight if table is not hovered HoverHandler { id: mouseHoverHandler acceptedDevices: PointerDevice.AllDevices @@ -95,18 +190,17 @@ ListView { policy: scrollBarMode === listView.AlwaysOn ? ScrollBar.AlwaysOn : scrollBarMode === listView.AsNeeded ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff - topInset: listView.showHeader ? listView.tableRowHeight : 0 - topPadding: listView.padding + (listView.showHeader ? listView.tableRowHeight : 0) + topInset: listView._headerHeight + topPadding: listView.padding + listView._headerHeight } ScrollIndicator.vertical: EaElements.ScrollIndicator { active: scrollBarMode === listView.Indicator - topInset: listView.showHeader ? listView.tableRowHeight : 0 - topPadding: listView.padding + (listView.showHeader ? listView.tableRowHeight : 0) + topInset: listView._headerHeight + topPadding: listView.padding + listView._headerHeight } - // Default info, if no rows added. - // Parented directly to listView so it doesn't scroll with content. + // Empty-state label. Rectangle { parent: listView visible: listView.count === 0 @@ -124,8 +218,7 @@ ListView { } } - // Table border, z above all content (header z:3, highlight z:2). - // Parented directly to listView so it doesn't scroll with content. + // Table border, z above all content. Rectangle { parent: listView z: 4 @@ -138,88 +231,10 @@ ListView { ItemSelectionModel { id: selectionModel model: listView.model - } - - Connections { - target: selectionModel - - function onSelectionChanged() { - if (selectionModel.selectedIndexes.length === 0) - anchorRow = -1 - } - } - - function applyWidths(row) { - for (let i = 0; i < row.children.length && i < resolvedColumnWidths.length; i++) - row.children[i].width = resolvedColumnWidths[i] - } - - // --- helper: convert row -> QModelIndex --- - function _index(row) { - if (!selectionModel.model || row < 0 || row >= count) - return null - return selectionModel.model.index(row, 0) - } - - // --- public API --- - function isSelected(row) { - let idx = _index(row) - return idx && idx.valid ? selectionModel.isSelected(idx) : false - } - - function selectWithModifiers(row, modifiers) { - let idx = _index(row) - if (!idx) return - - // --- SHIFT: range selection --- - if (listView.multiSelection && modifiers & Qt.ShiftModifier) { - if (anchorRow < 0) { - anchorRow = row - } - - let savedAnchor = anchorRow - let from = Math.min(anchorRow, row) - let to = Math.max(anchorRow, row) - - // If Ctrl is NOT pressed -> replace selection - if (!(modifiers & Qt.ControlModifier)) { - selectionModel.clearSelection() - } - - for (let i = from; i <= to; i++) { - let rIdx = _index(i) - if (rIdx) { - selectionModel.select( - rIdx, - ItemSelectionModel.Select | ItemSelectionModel.Rows - ) - } - } - anchorRow = savedAnchor - return + onSelectionChanged: { + if (selectedIndexes.length === 0) + listView.anchorRow = -1 } - - // --- CTRL: toggle --- - if (listView.multiSelection && modifiers & Qt.ControlModifier) { - selectionModel.select( - idx, - ItemSelectionModel.Toggle | ItemSelectionModel.Rows - ) - anchorRow = row - return - } - - // --- DEFAULT: single selection --- - selectionModel.select( - idx, - ItemSelectionModel.ClearAndSelect | ItemSelectionModel.Rows - ) - anchorRow = row - } - - function clearSelection() { - selectionModel.clearSelection() - anchorRow = -1 } } diff --git a/src/EasyApp/Gui/Components/ListViewHeader.qml b/src/EasyApp/Gui/Components/ListViewHeader.qml index a28463b4..adb5f75d 100644 --- a/src/EasyApp/Gui/Components/ListViewHeader.qml +++ b/src/EasyApp/Gui/Components/ListViewHeader.qml @@ -9,12 +9,10 @@ Rectangle { default property alias contentRowData: contentRow.data property Item listView: ListView.view ?? null - visible: listView && listView.showHeader - z: 3 // To display header above delegate and highlighted area implicitWidth: parent === null ? 0 : parent.width - implicitHeight: listView && listView.showHeader ? listView.tableRowHeight : 0 + implicitHeight: listView ? listView.tableRowHeight : 0 color: EaStyle.Colors.contentBackground Behavior on color { EaAnimations.ThemeChange {} } From 844d107e20d7beb1077fe85a759b769820e35f50 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 13 Apr 2026 15:55:19 +0200 Subject: [PATCH 21/40] Made shift+click additive, instead of a new selection --- src/EasyApp/Gui/Components/ListView.qml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 4b93ae70..fcbe09b3 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -106,10 +106,6 @@ ListView { let from = Math.min(anchorRow, row) let to = Math.max(anchorRow, row) - if (!(modifiers & Qt.ControlModifier)) { - selectionModel.clearSelection() - } - for (let i = from; i <= to; i++) { let rIdx = _index(i) if (rIdx) { From 1902300ab95094994223775770f0c253ad58838f Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 13 Apr 2026 16:06:22 +0200 Subject: [PATCH 22/40] added a small angle cap to indicate deselected anchor --- src/EasyApp/Gui/Components/ListView.qml | 9 ++++-- .../Gui/Components/ListViewDelegate.qml | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index fcbe09b3..98485106 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -106,6 +106,10 @@ ListView { let from = Math.min(anchorRow, row) let to = Math.max(anchorRow, row) + if (!(modifiers & Qt.ControlModifier)) { + selectionModel.clearSelection() + } + for (let i = from; i <= to; i++) { let rIdx = _index(i) if (rIdx) { @@ -138,11 +142,12 @@ ListView { anchorRow = row } - // ── Internals ─────────────────────────────────────────────────────── - // Anchor row index for shift-selection range tracking. + // Used by: ListViewDelegate (anchor indicator when row is not selected) property int anchorRow: -1 + // ── Internals ─────────────────────────────────────────────────────── + // Convert row int to QModelIndex for selectionModel. function _index(row) { if (!selectionModel.model || row < 0 || row >= count) diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index e8959b37..edaa341e 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -40,6 +40,34 @@ Rectangle { spacing: EaStyle.Sizes.tableColumnSpacing } + // Anchor indicator: small triangle in top-right corner when row is + // the shift-selection anchor but not currently selected. + Item { + visible: { + // Read selectedIndexes to create binding dependency for reactivity. + listView.selectedIndexes + return index === listView.anchorRow && !listView.isSelected(index) + } + anchors.top: parent.top + anchors.right: parent.right + width: 8 + height: 8 + clip: true + layer.enabled: true + layer.smooth: false + + Rectangle { + width: parent.width * 1.5 + height: parent.height * 1.5 + rotation: 45 + x: Math.round(parent.width / 2) + y: Math.round(-parent.height * 0.75) + antialiasing: false + color: EaStyle.Colors.themeAccentMinor + Behavior on color { EaAnimations.ThemeChange {} } + } + } + //Mouse area to react on click events MouseArea { id: mouseArea From 0360a0ffd392f5d158fa80245fe4502cc2685216 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 13 Apr 2026 16:35:02 +0200 Subject: [PATCH 23/40] Fixed enum error in ListView that blocked depicting the scrollbar/indicator --- src/EasyApp/Gui/Components/ListView.qml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 98485106..9e58e2b3 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -25,15 +25,16 @@ ListView { property alias defaultInfoText: defaultInfoLabel.text // Scroll bar display mode. - // Externally, use full module path: EaComponents.ListView.Indicator, etc. - // Internally, use id-based path: listView.Indicator, etc. - // Unqualified "ListView.Indicator" resolves to QtQuick's ListView, not this component. + // Internally, use enum type name: ScrollBarMode.Indicator, etc. + // Externally, use full module path: EaComponents.ListView.ScrollBarMode.Indicator, etc. + // Cannot use id (listView.Indicator) — enums are type-scoped, not instance-scoped. + // Cannot use ListView.Indicator — resolves to QtQuick's ListView, not this component. enum ScrollBarMode { Indicator, AsNeeded, AlwaysOn } - property int scrollBarMode: listView.Indicator + property int scrollBarMode: ScrollBarMode.Indicator // Allow ctrl/shift multi-select. property bool multiSelection: true @@ -188,15 +189,18 @@ ListView { } ScrollBar.vertical: EaElements.ScrollBar { - policy: scrollBarMode === listView.AlwaysOn ? ScrollBar.AlwaysOn - : scrollBarMode === listView.AsNeeded ? ScrollBar.AsNeeded + // ScrollBarMode enum not in scope inside child objects; use int values. + // AlwaysOn=2, AsNeeded=1 + policy: listView.scrollBarMode === 2 ? ScrollBar.AlwaysOn + : listView.scrollBarMode === 1 ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff topInset: listView._headerHeight topPadding: listView.padding + listView._headerHeight } ScrollIndicator.vertical: EaElements.ScrollIndicator { - active: scrollBarMode === listView.Indicator + // Indicator=0 + active: listView.scrollBarMode === 0 topInset: listView._headerHeight topPadding: listView.padding + listView._headerHeight } From b6dbf9fdaefd848602b1111b35b3a2f341c8f25a Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 13 Apr 2026 16:39:54 +0200 Subject: [PATCH 24/40] Vibecoding cleanup --- src/EasyApp/Gui/Components/ListView.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 9e58e2b3..882f7f2f 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -52,6 +52,10 @@ ListView { // ── Companion API ─────────────────────────────────────────────────── // Used by ListViewHeader and ListViewDelegate. Not intended for direct consumer use. + // Anchor row index for shift-selection range tracking. + // Used by: ListViewDelegate (anchor indicator when row is not selected) + property int anchorRow: -1 + // Row height in px, derived from tallRows. // Used by: ListViewDelegate (implicitHeight), ListViewHeader (own height) property int tableRowHeight: tallRows ? @@ -143,10 +147,6 @@ ListView { anchorRow = row } - // Anchor row index for shift-selection range tracking. - // Used by: ListViewDelegate (anchor indicator when row is not selected) - property int anchorRow: -1 - // ── Internals ─────────────────────────────────────────────────────── // Convert row int to QModelIndex for selectionModel. From e3d6360e0bdf2219372ce055c54a823953353bc7 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 14 Apr 2026 13:52:52 +0200 Subject: [PATCH 25/40] scope-aware selection visuals via focus tracking --- src/EasyApp/Gui/Components/ListView.qml | 17 +++++++++++++++++ src/EasyApp/Gui/Components/ListViewDelegate.qml | 15 +++++++++++---- src/EasyApp/Gui/Components/ListViewHeader.qml | 9 +++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 882f7f2f..4f3e85a1 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -39,6 +39,16 @@ ListView { // Allow ctrl/shift multi-select. property bool multiSelection: true + // Claim the enclosing FocusScope's default focus target. + focus: true + + // Drives whether the delegate renders selection/anchor visuals. + // Default: this list's own activeFocus. When hosted inside a FocusScope + // with focusable siblings (buttons, filters), override with the scope's + // activeFocus so visuals stay lit while focus is elsewhere in the panel, + // e.g. `selectionActive: myScope.activeFocus`. + property bool selectionActive: activeFocus + // Column widths definition. Each entry is a width in px, or -1 to fill remaining space. // Example: columnWidths: [40, -1, 100] property var columnWidths: [] @@ -188,6 +198,13 @@ ListView { blocking: false } + // Any tap on the list (header, delegates, empty area) claims focus. + // Non-blocking: delegate MouseAreas and header interactions still fire. + TapHandler { + acceptedDevices: PointerDevice.AllDevices + onTapped: listView.forceActiveFocus() + } + ScrollBar.vertical: EaElements.ScrollBar { // ScrollBarMode enum not in scope inside child objects; use int values. // AlwaysOn=2, AsNeeded=1 diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index edaa341e..868c8459 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -17,7 +17,7 @@ Rectangle { // this color expression to re-evaluate whenever the selection changes. listView.selectedIndexes - let selected = index >= 0 && listView.isSelected(index) + let selected = index >= 0 && listView.isSelected(index) && listView.selectionActive let c1 = EaStyle.Colors.themeAccentMinor let c2 = EaStyle.Colors.themeBackgroundHovered2 let c3 = EaStyle.Colors.themeBackgroundHovered1 @@ -46,7 +46,9 @@ Rectangle { visible: { // Read selectedIndexes to create binding dependency for reactivity. listView.selectedIndexes - return index === listView.anchorRow && !listView.isSelected(index) + return listView.selectionActive + && index === listView.anchorRow + && !listView.isSelected(index) } anchors.top: parent.top anchors.right: parent.right @@ -68,14 +70,19 @@ Rectangle { } } - //Mouse area to react on click events + // MouseArea's exclusive grab on press blocks the ListView-level + // TapHandler from firing for delegate clicks, so focus transfer is + // driven from here. The ListView's own TapHandler still handles taps + // on the header, border, and empty area. MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: false onClicked: (mouse) => { - if (index >= 0) + if (index >= 0) { + listView.forceActiveFocus() listView.selectWithModifiers(index, mouse.modifiers) + } } } diff --git a/src/EasyApp/Gui/Components/ListViewHeader.qml b/src/EasyApp/Gui/Components/ListViewHeader.qml index adb5f75d..59fc3ee2 100644 --- a/src/EasyApp/Gui/Components/ListViewHeader.qml +++ b/src/EasyApp/Gui/Components/ListViewHeader.qml @@ -30,4 +30,13 @@ Rectangle { height: parent.height spacing: EaStyle.Sizes.tableColumnSpacing } + + // Header sits above delegate 0 (OverlayHeader). Without an input + // handler here, clicks fall through to that delegate's MouseArea, + // bypassing the ListView-level TapHandler. This claims the press + // so header clicks transfer focus to the list. + MouseArea { + anchors.fill: parent + onClicked: if (listView) listView.forceActiveFocus() + } } From 6faaf85751fa2261fcafef2de2a01b31cfe8ab8e Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 14 Apr 2026 14:04:15 +0200 Subject: [PATCH 26/40] changed the default of selectionActive to true --- src/EasyApp/Gui/Components/ListView.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 4f3e85a1..4237d45e 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -43,11 +43,12 @@ ListView { focus: true // Drives whether the delegate renders selection/anchor visuals. - // Default: this list's own activeFocus. When hosted inside a FocusScope + // Default: always true. When hosted inside a FocusScope // with focusable siblings (buttons, filters), override with the scope's // activeFocus so visuals stay lit while focus is elsewhere in the panel, - // e.g. `selectionActive: myScope.activeFocus`. - property bool selectionActive: activeFocus + // e.g. `selectionActive: myScope.activeFocus`. Or use this list's own focus, + // e.g. `selectionActive: activeFocus`. + property bool selectionActive: true // Column widths definition. Each entry is a width in px, or -1 to fill remaining space. // Example: columnWidths: [40, -1, 100] From 4fbf861f8a78cf748e18cb018b748b978b77dc6f Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Thu, 16 Apr 2026 11:05:31 +0200 Subject: [PATCH 27/40] Fix the mousearea intercepting clicks completely --- src/EasyApp/Gui/Components/ListView.qml | 1 + src/EasyApp/Gui/Components/ListViewDelegate.qml | 17 +++++++---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 4237d45e..fd0cf2d2 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -48,6 +48,7 @@ ListView { // activeFocus so visuals stay lit while focus is elsewhere in the panel, // e.g. `selectionActive: myScope.activeFocus`. Or use this list's own focus, // e.g. `selectionActive: activeFocus`. + // Used by: ListViewDelegate. property bool selectionActive: true // Column widths definition. Each entry is a width in px, or -1 to fill remaining space. diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 868c8459..13fcfd0f 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -70,18 +70,15 @@ Rectangle { } } - // MouseArea's exclusive grab on press blocks the ListView-level - // TapHandler from firing for delegate clicks, so focus transfer is - // driven from here. The ListView's own TapHandler still handles taps - // on the header, border, and empty area. - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: false - onClicked: (mouse) => { + // TapHandler (not MouseArea) so nested interactive children like + // TableViewButton receive their own press events — MouseArea's + // exclusive grab on press would swallow clicks on those buttons. + TapHandler { + id: tap + onTapped: { if (index >= 0) { listView.forceActiveFocus() - listView.selectWithModifiers(index, mouse.modifiers) + listView.selectWithModifiers(index, tap.point.modifiers) } } } From d4c74c6a9a81cf54053efb796703e4b07a82801b Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Thu, 16 Apr 2026 15:27:33 +0200 Subject: [PATCH 28/40] Added padding to left and right on delegate row of ListViewDelegate --- src/EasyApp/Gui/Components/ListViewDelegate.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 13fcfd0f..00775815 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -38,6 +38,8 @@ Rectangle { height: parent.height spacing: EaStyle.Sizes.tableColumnSpacing + leftPadding: EaStyle.Sizes.tableColumnSpacing + rightPadding: EaStyle.Sizes.tableColumnSpacing } // Anchor indicator: small triangle in top-right corner when row is From 2c51f26c399a34c62a0d3b8d35e344ae60312cb0 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 17 Apr 2026 15:17:11 +0200 Subject: [PATCH 29/40] Changed the selection coloring so that it would disappear while editing anything inside --- .../Gui/Components/ListViewDelegate.qml | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 00775815..7d0cefa2 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -9,6 +9,12 @@ Rectangle { default property alias contentRowData: contentRow.data property Item listView: ListView.view ?? null + // True while any focusable cell inside the row (typically a TextInput) + // owns activeFocus. Aggregated by the FocusScope wrapping contentRow. + // The delegate also factors this into its own selection visuals so + // inline editing isn't drawn over the accent row background. + readonly property alias editing: editScope.activeFocus + implicitWidth: listView.width implicitHeight: listView.tableRowHeight @@ -17,7 +23,7 @@ Rectangle { // this color expression to re-evaluate whenever the selection changes. listView.selectedIndexes - let selected = index >= 0 && listView.isSelected(index) && listView.selectionActive + let selected = index >= 0 && listView.isSelected(index) && listView.selectionActive && !editing let c1 = EaStyle.Colors.themeAccentMinor let c2 = EaStyle.Colors.themeBackgroundHovered2 let c3 = EaStyle.Colors.themeBackgroundHovered1 @@ -33,13 +39,18 @@ Rectangle { function onResolvedColumnWidthsChanged() { listView.applyWidths(contentRow) } } - Row { - id: contentRow + FocusScope { + id: editScope + anchors.fill: parent + + Row { + id: contentRow - height: parent.height - spacing: EaStyle.Sizes.tableColumnSpacing - leftPadding: EaStyle.Sizes.tableColumnSpacing - rightPadding: EaStyle.Sizes.tableColumnSpacing + height: parent.height + spacing: EaStyle.Sizes.tableColumnSpacing + leftPadding: EaStyle.Sizes.tableColumnSpacing + rightPadding: EaStyle.Sizes.tableColumnSpacing + } } // Anchor indicator: small triangle in top-right corner when row is @@ -51,6 +62,7 @@ Rectangle { return listView.selectionActive && index === listView.anchorRow && !listView.isSelected(index) + && !editing } anchors.top: parent.top anchors.right: parent.right From 7cf00f05cc0fb96eabda3e25cb645948566b35f8 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Fri, 17 Apr 2026 16:36:15 +0200 Subject: [PATCH 30/40] Fixing the hover change forcing editing finished on ListView cells --- src/EasyApp/Gui/Components/ListView.qml | 33 ++++++++++++++----- .../Gui/Components/ListViewDelegate.qml | 27 +++++++++------ src/EasyApp/Gui/Components/ListViewHeader.qml | 2 ++ .../Gui/Components/ListViewTextInput.qml | 30 +++++++++++++++++ src/EasyApp/Gui/Components/qmldir | 1 + 5 files changed, 75 insertions(+), 18 deletions(-) create mode 100644 src/EasyApp/Gui/Components/ListViewTextInput.qml diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index fd0cf2d2..0dba6e21 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -55,6 +55,10 @@ ListView { // Example: columnWidths: [40, -1, 100] property var columnWidths: [] + // Horizontal padding inside each row (header + delegate). Subtracted from + // flex-column budget so -1 columns don't overflow the row. + property real rowPadding: EaStyle.Sizes.tableColumnSpacing + // Clear all selection and reset anchor. function clearSelection() { selectionModel.clearSelection() @@ -68,6 +72,12 @@ ListView { // Used by: ListViewDelegate (anchor indicator when row is not selected) property int anchorRow: -1 + // Row index currently under the mouse. Visual-only. Never mutated from + // keyboard or selection paths — stays orthogonal to currentIndex and + // selectionModel so inline editors don't lose activeFocus on hover. + // Used by: ListViewDelegate (hover tint in row color binding) + property int hoveredIndex: -1 + // Row height in px, derived from tallRows. // Used by: ListViewDelegate (implicitHeight), ListViewHeader (own height) property int tableRowHeight: tallRows ? @@ -89,7 +99,7 @@ ListView { } const spacing = EaStyle.Sizes.tableColumnSpacing * (columnWidths.length - 1) const border = EaStyle.Sizes.borderThickness * 2 - const fill = flexCount > 0 ? Math.max(0, (width - fixed - spacing - border) / flexCount) : 0 + const fill = flexCount > 0 ? Math.max(0, (width - fixed - spacing - border - rowPadding * 2) / flexCount) : 0 return columnWidths.map(w => w > 0 ? w : fill) } @@ -185,21 +195,28 @@ ListView { boundsBehavior: Flickable.StopAtBounds enabled: count > 0 + // Hover highlight via the built-in highlight delegate, but detached + // from currentIndex: we drive position from hoveredIndex (mouse-only, + // never touches focus/selection) while still getting the smooth + // inter-row translation that highlightMoveDuration provides. + highlightFollowsCurrentItem: false highlightMoveDuration: EaStyle.Sizes.tableHighlightMoveDuration highlight: Rectangle { z: 2 - color: mouseHoverHandler.hovered ? + width: listView.width + height: listView.tableRowHeight + // Freeze at last hovered row on leave so the tint fades in place + // instead of sliding back to row 0. + y: listView.hoveredIndex >= 0 + ? listView.hoveredIndex * listView.tableRowHeight + : y + color: listView.hoveredIndex >= 0 ? EaStyle.Colors.tableHighlight : "transparent" + Behavior on y { NumberAnimation { duration: listView.highlightMoveDuration } } Behavior on color { EaAnimations.ThemeChange {} } } - HoverHandler { - id: mouseHoverHandler - acceptedDevices: PointerDevice.AllDevices - blocking: false - } - // Any tap on the list (header, delegates, empty area) claims focus. // Non-blocking: delegate MouseAreas and header interactions still fire. TapHandler { diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 7d0cefa2..cf77546c 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -24,11 +24,13 @@ Rectangle { listView.selectedIndexes let selected = index >= 0 && listView.isSelected(index) && listView.selectionActive && !editing - let c1 = EaStyle.Colors.themeAccentMinor - let c2 = EaStyle.Colors.themeBackgroundHovered2 - let c3 = EaStyle.Colors.themeBackgroundHovered1 - return selected ? c1 : (index % 2 ? c2 : c3) + let selectedColor = EaStyle.Colors.themeAccentMinor + let evenRowColor = EaStyle.Colors.themeBackgroundHovered2 + let oddRowColor = EaStyle.Colors.themeBackgroundHovered1 + let alternatingColor = index % 2 ? evenRowColor : oddRowColor + + return selected ? selectedColor : alternatingColor } Behavior on color { EaAnimations.ThemeChange {} } @@ -48,8 +50,8 @@ Rectangle { height: parent.height spacing: EaStyle.Sizes.tableColumnSpacing - leftPadding: EaStyle.Sizes.tableColumnSpacing - rightPadding: EaStyle.Sizes.tableColumnSpacing + leftPadding: listView ? listView.rowPadding : 0 + rightPadding: listView ? listView.rowPadding : 0 } } @@ -97,16 +99,21 @@ Rectangle { } } - // Mouse-only navigation: highlight follows hover. - // If keyboard navigation is added, guard this with a keyboardActive flag. + // Visual-only hover tracking. Writes to listView.hoveredIndex, never + // currentIndex or selectionModel — keeping those independent prevents + // hover from stealing activeFocus from inline editors (e.g. TextInput) + // in a different row during editing. HoverHandler { id: mouseHoverHandler acceptedDevices: PointerDevice.AllDevices cursorShape: Qt.PointingHandCursor blocking: false onHoveredChanged: { - if (hovered && index >= 0) - listView.currentIndex = index + if (index < 0) return + if (hovered) + listView.hoveredIndex = index + else if (listView.hoveredIndex === index) + listView.hoveredIndex = -1 } } } diff --git a/src/EasyApp/Gui/Components/ListViewHeader.qml b/src/EasyApp/Gui/Components/ListViewHeader.qml index 59fc3ee2..cebe4799 100644 --- a/src/EasyApp/Gui/Components/ListViewHeader.qml +++ b/src/EasyApp/Gui/Components/ListViewHeader.qml @@ -29,6 +29,8 @@ Rectangle { height: parent.height spacing: EaStyle.Sizes.tableColumnSpacing + leftPadding: listView ? listView.rowPadding : 0 + rightPadding: listView ? listView.rowPadding : 0 } // Header sits above delegate 0 (OverlayHeader). Without an input diff --git a/src/EasyApp/Gui/Components/ListViewTextInput.qml b/src/EasyApp/Gui/Components/ListViewTextInput.qml new file mode 100644 index 00000000..ee3060b9 --- /dev/null +++ b/src/EasyApp/Gui/Components/ListViewTextInput.qml @@ -0,0 +1,30 @@ +import QtQuick + +import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Animations as EaAnimations +import EasyApp.Gui.Elements as EaElements + +EaElements.TextInput { + id: control + + property string headerText: "" + + height: parent.height + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + hoverEnabled: true + + // ListView has its own row selection, so we don't need the TableView-style + // "highlight the last-edited cell" behaviour — hence this separate component + // with a color override. Track activeFocus (real keyboard focus) rather than + // the per-FocusScope `focus` flag used in TextInput.qml, so sibling + // ListViewDelegates don't stay blue after editing ends. + color: warned ? + EaStyle.Colors.red : + !enabled || readOnly || minored ? + EaStyle.Colors.themeForegroundMinor : + activeFocus || selected || hovered ? + EaStyle.Colors.themeForegroundHovered : + EaStyle.Colors.themeForeground + Behavior on color { EaAnimations.ThemeChange {} } +} diff --git a/src/EasyApp/Gui/Components/qmldir b/src/EasyApp/Gui/Components/qmldir index 576f2a8f..9c5960d8 100644 --- a/src/EasyApp/Gui/Components/qmldir +++ b/src/EasyApp/Gui/Components/qmldir @@ -36,6 +36,7 @@ TableViewComboBox 1.0 TableViewComboBox.qml TableViewButton 1.0 TableViewButton.qml TableViewLabelControl 1.0 TableViewLabelControl.qml TableViewTextInput 1.0 TableViewTextInput.qml +ListViewTextInput 1.0 ListViewTextInput.qml JsonListModel 1.0 JsonListModel.qml From 33757dc8a2a7a40f8489981807bda03467247a75 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 20 Apr 2026 11:52:02 +0200 Subject: [PATCH 31/40] Moved hover highlight from listview to delegate --- src/EasyApp/Gui/Components/ListView.qml | 22 ------------------- .../Gui/Components/ListViewDelegate.qml | 10 +++++++++ 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 0dba6e21..dfdbe466 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -195,28 +195,6 @@ ListView { boundsBehavior: Flickable.StopAtBounds enabled: count > 0 - // Hover highlight via the built-in highlight delegate, but detached - // from currentIndex: we drive position from hoveredIndex (mouse-only, - // never touches focus/selection) while still getting the smooth - // inter-row translation that highlightMoveDuration provides. - highlightFollowsCurrentItem: false - highlightMoveDuration: EaStyle.Sizes.tableHighlightMoveDuration - highlight: Rectangle { - z: 2 - width: listView.width - height: listView.tableRowHeight - // Freeze at last hovered row on leave so the tint fades in place - // instead of sliding back to row 0. - y: listView.hoveredIndex >= 0 - ? listView.hoveredIndex * listView.tableRowHeight - : y - color: listView.hoveredIndex >= 0 ? - EaStyle.Colors.tableHighlight : - "transparent" - Behavior on y { NumberAnimation { duration: listView.highlightMoveDuration } } - Behavior on color { EaAnimations.ThemeChange {} } - } - // Any tap on the list (header, delegates, empty area) claims focus. // Non-blocking: delegate MouseAreas and header interactions still fire. TapHandler { diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index cf77546c..72e69c0a 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -41,6 +41,16 @@ Rectangle { function onResolvedColumnWidthsChanged() { listView.applyWidths(contentRow) } } + // Hover tint. Lives in the delegate so position is implicit from the + // delegate's own bounds — no y math, no uniform-row-height assumption. + Rectangle { + anchors.fill: parent + color: EaStyle.Colors.tableHighlight + opacity: listView && listView.hoveredIndex === index ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: EaStyle.Sizes.tableHighlightMoveDuration } } + Behavior on color { EaAnimations.ThemeChange {} } + } + FocusScope { id: editScope anchors.fill: parent From b4cc4b5f01550098275acb6353d4d1bbd9281293 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 20 Apr 2026 13:27:57 +0200 Subject: [PATCH 32/40] guard anchor reset on adding/removing from model --- src/EasyApp/Gui/Components/ListView.qml | 1 + src/EasyApp/Gui/Components/ListViewDelegate.qml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index dfdbe466..196fc884 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -71,6 +71,7 @@ ListView { // Anchor row index for shift-selection range tracking. // Used by: ListViewDelegate (anchor indicator when row is not selected) property int anchorRow: -1 + onCountChanged: if (anchorRow >= count) anchorRow = -1 // Row index currently under the mouse. Visual-only. Never mutated from // keyboard or selection paths — stays orthogonal to currentIndex and diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 72e69c0a..557df94a 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -7,7 +7,8 @@ Rectangle { id: control default property alias contentRowData: contentRow.data - property Item listView: ListView.view ?? null + // Needs to be instantiated inside of a EaComponents.ListView, won't work otherwise + property Item listView: ListView.view // True while any focusable cell inside the row (typically a TextInput) // owns activeFocus. Aggregated by the FocusScope wrapping contentRow. From 53a9861b3244f837137cb4299bee925fd911b592 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 20 Apr 2026 15:43:33 +0200 Subject: [PATCH 33/40] Got rid of indicator alltogether to use scrollbar with an interactive flick --- src/EasyApp/Gui/Components/ListView.qml | 32 +++++++------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 196fc884..a975a0d8 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -24,17 +24,11 @@ ListView { // Text shown when ListView model is empty. property alias defaultInfoText: defaultInfoLabel.text - // Scroll bar display mode. - // Internally, use enum type name: ScrollBarMode.Indicator, etc. - // Externally, use full module path: EaComponents.ListView.ScrollBarMode.Indicator, etc. - // Cannot use id (listView.Indicator) — enums are type-scoped, not instance-scoped. - // Cannot use ListView.Indicator — resolves to QtQuick's ListView, not this component. - enum ScrollBarMode { - Indicator, - AsNeeded, - AlwaysOn - } - property int scrollBarMode: ScrollBarMode.Indicator + // ScrollBar.AsNeeded / ScrollBar.AlwaysOff / ScrollBar.AlwaysOn + property int scrollBarPolicy: ScrollBar.AsNeeded + + // false = indicator style: thin, non-draggable, shows only while scrolling + property bool scrollBarInteractive: true // Allow ctrl/shift multi-select. property bool multiSelection: true @@ -204,20 +198,10 @@ ListView { } ScrollBar.vertical: EaElements.ScrollBar { - // ScrollBarMode enum not in scope inside child objects; use int values. - // AlwaysOn=2, AsNeeded=1 - policy: listView.scrollBarMode === 2 ? ScrollBar.AlwaysOn - : listView.scrollBarMode === 1 ? ScrollBar.AsNeeded - : ScrollBar.AlwaysOff - topInset: listView._headerHeight - topPadding: listView.padding + listView._headerHeight - } - - ScrollIndicator.vertical: EaElements.ScrollIndicator { - // Indicator=0 - active: listView.scrollBarMode === 0 + policy: listView.scrollBarPolicy + interactive: listView.scrollBarInteractive topInset: listView._headerHeight - topPadding: listView.padding + listView._headerHeight + topPadding: listView._headerHeight } // Empty-state label. From 91bf25eaca72dd663ea14b9d83d1ebd121c6ca54 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 21 Apr 2026 09:39:16 +0200 Subject: [PATCH 34/40] Added hover highlight on editing and the color flash from TextInput --- src/EasyApp/Gui/Components/ListViewDelegate.qml | 2 +- src/EasyApp/Gui/Components/ListViewTextInput.qml | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 557df94a..474dbf09 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -47,7 +47,7 @@ Rectangle { Rectangle { anchors.fill: parent color: EaStyle.Colors.tableHighlight - opacity: listView && listView.hoveredIndex === index ? 1 : 0 + opacity: (listView && listView.hoveredIndex === index) || editing ? 1 : 0 Behavior on opacity { NumberAnimation { duration: EaStyle.Sizes.tableHighlightMoveDuration } } Behavior on color { EaAnimations.ThemeChange {} } } diff --git a/src/EasyApp/Gui/Components/ListViewTextInput.qml b/src/EasyApp/Gui/Components/ListViewTextInput.qml index ee3060b9..6fd4b0b9 100644 --- a/src/EasyApp/Gui/Components/ListViewTextInput.qml +++ b/src/EasyApp/Gui/Components/ListViewTextInput.qml @@ -19,12 +19,14 @@ EaElements.TextInput { // with a color override. Track activeFocus (real keyboard focus) rather than // the per-FocusScope `focus` flag used in TextInput.qml, so sibling // ListViewDelegates don't stay blue after editing ends. - color: warned ? - EaStyle.Colors.red : - !enabled || readOnly || minored ? - EaStyle.Colors.themeForegroundMinor : - activeFocus || selected || hovered ? - EaStyle.Colors.themeForegroundHovered : - EaStyle.Colors.themeForeground + color: enterFlash ? + EaStyle.Colors.themeForeground : + warned ? + EaStyle.Colors.red : + !enabled || readOnly || minored ? + EaStyle.Colors.themeForegroundMinor : + activeFocus || selected || hovered ? + EaStyle.Colors.themeForegroundHovered : + EaStyle.Colors.themeForeground Behavior on color { EaAnimations.ThemeChange {} } } From 0d33f56ea2adae51087213211d61975da5b0b608 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 21 Apr 2026 15:26:44 +0200 Subject: [PATCH 35/40] Changed light-themed selection to be more readable with selected textinput and disabled elements --- src/EasyApp/Gui/Components/ListView.qml | 7 -- .../Gui/Components/ListViewDelegate.qml | 109 +++++++++++------- src/EasyApp/Gui/Style/Colors.qml | 5 + 3 files changed, 70 insertions(+), 51 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index a975a0d8..a8289c7e 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -190,13 +190,6 @@ ListView { boundsBehavior: Flickable.StopAtBounds enabled: count > 0 - // Any tap on the list (header, delegates, empty area) claims focus. - // Non-blocking: delegate MouseAreas and header interactions still fire. - TapHandler { - acceptedDevices: PointerDevice.AllDevices - onTapped: listView.forceActiveFocus() - } - ScrollBar.vertical: EaElements.ScrollBar { policy: listView.scrollBarPolicy interactive: listView.scrollBarInteractive diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 474dbf09..a0faf2e6 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -16,27 +16,74 @@ Rectangle { // inline editing isn't drawn over the accent row background. readonly property alias editing: editScope.activeFocus + // Row is in the selection model. Reads selectedIndexes to create a + // binding dependency so this re-evaluates when selection changes + // (isSelected() alone isn't tracked by QML). Used for the left accent + // bar and the hover overlay color — both stay selection-aware even + // while an inline editor owns focus. + readonly property bool inSelection: { + listView.selectedIndexes + return index >= 0 + && listView.isSelected(index) + && listView.selectionActive + } + + // Selection for the base row fill. Suppressed during editing so the + // editor isn't drawn over the highlight color. + readonly property bool selected: inSelection && !editing + implicitWidth: listView.width implicitHeight: listView.tableRowHeight color: { - // Read selectedIndexes to create a binding dependency — forces - // this color expression to re-evaluate whenever the selection changes. - listView.selectedIndexes - - let selected = index >= 0 && listView.isSelected(index) && listView.selectionActive && !editing - - let selectedColor = EaStyle.Colors.themeAccentMinor + let selectedColor = EaStyle.Colors.themeRowHighlight let evenRowColor = EaStyle.Colors.themeBackgroundHovered2 let oddRowColor = EaStyle.Colors.themeBackgroundHovered1 let alternatingColor = index % 2 ? evenRowColor : oddRowColor - return selected ? selectedColor : alternatingColor + return control.selected ? selectedColor : alternatingColor } Behavior on color { EaAnimations.ThemeChange {} } + // Vertical accent bar on the left edge. Dual-purpose: + // - inSelection → solid themeAccent (selection indicator, persists + // during inline editing so the selected row stays identifiable). + // - shift-selection anchor row (not selected, not editing) + // → themeAccentMinor (replaces the former top-right triangle). + // z:2 keeps the bar above the hover overlay (z:1) so selection + // remains visible while hovering. + Rectangle { + z: 2 + anchors.left: parent.left + anchors.top: parent.top + anchors.bottom: parent.bottom + width: 4 + color: EaStyle.Colors.themeAccent + visible: { + listView.selectedIndexes + if (control.inSelection) return true + return listView.selectionActive + && index === listView.anchorRow + && !editing + } + Behavior on color { EaAnimations.ThemeChange {} } + } + Component.onCompleted: if (listView) listView.applyWidths(contentRow) + // A cell editor (e.g. ListViewTextInput) claiming activeFocus flips + // `editing` true. Mirror that into the row selection so the edited + // row is also the selected row. No modifier → single-select. Don't + // touch focus here — the editor already owns it. + onEditingChanged: { + // Don't touch currentIndex — reassigning it mid-click can make the + // ListView re-target focus onto the delegate, stealing activeFocus + // from the editor the user just clicked into. + if (editing && index >= 0 && !control.inSelection) { + listView.selectWithModifiers(index, Qt.NoModifier) + } + } + Connections { target: listView function onResolvedColumnWidthsChanged() { listView.applyWidths(contentRow) } @@ -46,7 +93,9 @@ Rectangle { // delegate's own bounds — no y math, no uniform-row-height assumption. Rectangle { anchors.fill: parent - color: EaStyle.Colors.tableHighlight + color: control.inSelection && !editing + ? EaStyle.Colors.themeRowHighlightHovered + : EaStyle.Colors.themeRowHovered opacity: (listView && listView.hoveredIndex === index) || editing ? 1 : 0 Behavior on opacity { NumberAnimation { duration: EaStyle.Sizes.tableHighlightMoveDuration } } Behavior on color { EaAnimations.ThemeChange {} } @@ -66,47 +115,19 @@ Rectangle { } } - // Anchor indicator: small triangle in top-right corner when row is - // the shift-selection anchor but not currently selected. - Item { - visible: { - // Read selectedIndexes to create binding dependency for reactivity. - listView.selectedIndexes - return listView.selectionActive - && index === listView.anchorRow - && !listView.isSelected(index) - && !editing - } - anchors.top: parent.top - anchors.right: parent.right - width: 8 - height: 8 - clip: true - layer.enabled: true - layer.smooth: false - - Rectangle { - width: parent.width * 1.5 - height: parent.height * 1.5 - rotation: 45 - x: Math.round(parent.width / 2) - y: Math.round(-parent.height * 0.75) - antialiasing: false - color: EaStyle.Colors.themeAccentMinor - Behavior on color { EaAnimations.ThemeChange {} } - } - } - // TapHandler (not MouseArea) so nested interactive children like // TableViewButton receive their own press events — MouseArea's // exclusive grab on press would swallow clicks on those buttons. TapHandler { id: tap onTapped: { - if (index >= 0) { - listView.forceActiveFocus() - listView.selectWithModifiers(index, tap.point.modifiers) - } + if (index < 0) return + listView.currentIndex = index + // Don't steal focus when the tap landed on an inline editor + // (editing flips true as the editor gains activeFocus). The + // row still becomes selected via onEditingChanged. + if (!editing) listView.forceActiveFocus() + listView.selectWithModifiers(index, tap.point.modifiers) } } diff --git a/src/EasyApp/Gui/Style/Colors.qml b/src/EasyApp/Gui/Style/Colors.qml index 2923e3f3..31ae4b5e 100644 --- a/src/EasyApp/Gui/Style/Colors.qml +++ b/src/EasyApp/Gui/Style/Colors.qml @@ -54,6 +54,11 @@ QtObject { property color themeForegroundHovered: themeAccent property color themeForegroundHighlight: isDarkPalette ? '#FFAB91' : '#FF5722' + property color themeRowHovered: isDarkPalette ? "#2C3E48" : "#E6F5FC" + property color themeRowHighlight: isDarkPalette ? "#1F4E66" : "#DCF0FA" + property color themeRowHighlightHovered: isDarkPalette ? "#2F6A88" : "#C9E6F5" + + // Application window property color appBorder: isDarkPalette ? "#292929" : "#ddd" From 21888507f24dbd051773bb030f069fadb2a1ee6f Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 21 Apr 2026 17:06:06 +0200 Subject: [PATCH 36/40] adjusted colors for dark mode selection, trying to fix selection/editing bug --- src/EasyApp/Gui/Components/ListView.qml | 2 ++ .../Gui/Components/ListViewDelegate.qml | 18 ++++++++---------- src/EasyApp/Gui/Style/Colors.qml | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index a8289c7e..79498b6d 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -221,6 +221,8 @@ ListView { z: 4 anchors.fill: parent color: "transparent" + // Fixes disappearing border lines + antialiasing: true border.color: EaStyle.Colors.appBarComboBoxBorder Behavior on border.color { EaAnimations.ThemeChange {} } } diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index a0faf2e6..d563c183 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -57,7 +57,7 @@ Rectangle { anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom - width: 4 + width: 3 color: EaStyle.Colors.themeAccent visible: { listView.selectedIndexes @@ -73,12 +73,8 @@ Rectangle { // A cell editor (e.g. ListViewTextInput) claiming activeFocus flips // `editing` true. Mirror that into the row selection so the edited - // row is also the selected row. No modifier → single-select. Don't - // touch focus here — the editor already owns it. + // row is also the selected row. onEditingChanged: { - // Don't touch currentIndex — reassigning it mid-click can make the - // ListView re-target focus onto the delegate, stealing activeFocus - // from the editor the user just clicked into. if (editing && index >= 0 && !control.inSelection) { listView.selectWithModifiers(index, Qt.NoModifier) } @@ -123,10 +119,12 @@ Rectangle { onTapped: { if (index < 0) return listView.currentIndex = index - // Don't steal focus when the tap landed on an inline editor - // (editing flips true as the editor gains activeFocus). The - // row still becomes selected via onEditingChanged. - if (!editing) listView.forceActiveFocus() + // Any tap that reaches this handler lands on the row background + // (not an inline editor — T.TextField's grab swallows onTapped + // for clicks on the editor itself). So claiming focus here ends + // any in-progress inline edit in this or another row. + //if (!editing) listView.forceActiveFocus() + listView.forceActiveFocus() listView.selectWithModifiers(index, tap.point.modifiers) } } diff --git a/src/EasyApp/Gui/Style/Colors.qml b/src/EasyApp/Gui/Style/Colors.qml index 31ae4b5e..c3531b4a 100644 --- a/src/EasyApp/Gui/Style/Colors.qml +++ b/src/EasyApp/Gui/Style/Colors.qml @@ -54,9 +54,9 @@ QtObject { property color themeForegroundHovered: themeAccent property color themeForegroundHighlight: isDarkPalette ? '#FFAB91' : '#FF5722' - property color themeRowHovered: isDarkPalette ? "#2C3E48" : "#E6F5FC" - property color themeRowHighlight: isDarkPalette ? "#1F4E66" : "#DCF0FA" - property color themeRowHighlightHovered: isDarkPalette ? "#2F6A88" : "#C9E6F5" + property color themeRowHovered: isDarkPalette ? "#394247" : "#E6F5FC" + property color themeRowHighlight: isDarkPalette ? "#374952" : "#DCF0FA" + property color themeRowHighlightHovered: isDarkPalette ? "#3D5E6E" : "#C9E6F5" // Application window From 4d29e6e2de014c207e79a6dc834108839ced5f55 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Tue, 21 Apr 2026 17:15:47 +0200 Subject: [PATCH 37/40] another dark theme adjustment for selection colors --- src/EasyApp/Gui/Style/Colors.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EasyApp/Gui/Style/Colors.qml b/src/EasyApp/Gui/Style/Colors.qml index c3531b4a..78d5b347 100644 --- a/src/EasyApp/Gui/Style/Colors.qml +++ b/src/EasyApp/Gui/Style/Colors.qml @@ -55,8 +55,8 @@ QtObject { property color themeForegroundHighlight: isDarkPalette ? '#FFAB91' : '#FF5722' property color themeRowHovered: isDarkPalette ? "#394247" : "#E6F5FC" - property color themeRowHighlight: isDarkPalette ? "#374952" : "#DCF0FA" - property color themeRowHighlightHovered: isDarkPalette ? "#3D5E6E" : "#C9E6F5" + property color themeRowHighlight: isDarkPalette ? "#3A484F" : "#DCF0FA" + property color themeRowHighlightHovered: isDarkPalette ? "#3E5059" : "#C9E6F5" // Application window From 6167a3e21fd18bef475441833e08fb3a68311e5b Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Wed, 22 Apr 2026 09:17:34 +0200 Subject: [PATCH 38/40] Fix for mixed up selections upon TextInput editing --- src/EasyApp/Gui/Components/ListViewDelegate.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index d563c183..89504d19 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -80,6 +80,16 @@ Rectangle { } } + // If this row leaves the selection while its inline editor still owns + // focus (e.g. user tapped another row's background, and listView's + // forceActiveFocus didn't pull focus out of this row's FocusScope), + // release it locally so the editor visuals and activeFocus drop. + onInSelectionChanged: { + if (!inSelection && editing) { + editScope.focus = false + } + } + Connections { target: listView function onResolvedColumnWidthsChanged() { listView.applyWidths(contentRow) } From 997385689b35926de57590678ec85850099fda6c Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 27 Apr 2026 12:11:44 +0200 Subject: [PATCH 39/40] Changed the selection to be optional onediting, introduced a flag for it --- src/EasyApp/Gui/Components/ListView.qml | 26 ++++++++++++++----- .../Gui/Components/ListViewDelegate.qml | 2 +- src/EasyApp/Gui/Components/ListViewHeader.qml | 6 ++++- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 79498b6d..2ff2b271 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -33,6 +33,10 @@ ListView { // Allow ctrl/shift multi-select. property bool multiSelection: true + // When false, clicking a cell editor (TextInput) does not select the row. + // Editing and selection remain orthogonal. + property bool selectOnEdit: false + // Claim the enclosing FocusScope's default focus target. focus: true @@ -146,13 +150,21 @@ ListView { return } - // CTRL: toggle - if (listView.multiSelection && modifiers & Qt.ControlModifier) { - selectionModel.select( - idx, - ItemSelectionModel.Toggle | ItemSelectionModel.Rows - ) - anchorRow = row + // CTRL: toggle. Multi mode: add/remove from existing selection. + // Single mode: deselect same row, or replace selection with new row. + if (modifiers & Qt.ControlModifier) { + if (listView.multiSelection) { + selectionModel.select(idx, ItemSelectionModel.Toggle | ItemSelectionModel.Rows) + anchorRow = row + return + } + if (selectionModel.isSelected(idx)) { + selectionModel.clearSelection() + anchorRow = -1 + } else { + selectionModel.select(idx, ItemSelectionModel.ClearAndSelect | ItemSelectionModel.Rows) + anchorRow = row + } return } diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 89504d19..396fd8b1 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -75,7 +75,7 @@ Rectangle { // `editing` true. Mirror that into the row selection so the edited // row is also the selected row. onEditingChanged: { - if (editing && index >= 0 && !control.inSelection) { + if (editing && index >= 0 && !control.inSelection && listView.selectOnEdit) { listView.selectWithModifiers(index, Qt.NoModifier) } } diff --git a/src/EasyApp/Gui/Components/ListViewHeader.qml b/src/EasyApp/Gui/Components/ListViewHeader.qml index cebe4799..61149731 100644 --- a/src/EasyApp/Gui/Components/ListViewHeader.qml +++ b/src/EasyApp/Gui/Components/ListViewHeader.qml @@ -39,6 +39,10 @@ Rectangle { // so header clicks transfer focus to the list. MouseArea { anchors.fill: parent - onClicked: if (listView) listView.forceActiveFocus() + onClicked: { + if (!listView) return + listView.clearSelection() + listView.forceActiveFocus() + } } } From 8a633d88f2ae52d071df194f30d20b42d8ba42a6 Mon Sep 17 00:00:00 2001 From: Ales Kutsepau Date: Mon, 27 Apr 2026 14:49:53 +0200 Subject: [PATCH 40/40] Removed hoveredindex, as hovered can be referenced within delegate anyway --- src/EasyApp/Gui/Components/ListView.qml | 6 ------ src/EasyApp/Gui/Components/ListViewDelegate.qml | 9 +-------- src/EasyApp/Gui/Components/qmldir | 2 +- src/EasyApp/Gui/Style/Colors.qml | 1 - 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/EasyApp/Gui/Components/ListView.qml b/src/EasyApp/Gui/Components/ListView.qml index 2ff2b271..87456e6a 100644 --- a/src/EasyApp/Gui/Components/ListView.qml +++ b/src/EasyApp/Gui/Components/ListView.qml @@ -71,12 +71,6 @@ ListView { property int anchorRow: -1 onCountChanged: if (anchorRow >= count) anchorRow = -1 - // Row index currently under the mouse. Visual-only. Never mutated from - // keyboard or selection paths — stays orthogonal to currentIndex and - // selectionModel so inline editors don't lose activeFocus on hover. - // Used by: ListViewDelegate (hover tint in row color binding) - property int hoveredIndex: -1 - // Row height in px, derived from tallRows. // Used by: ListViewDelegate (implicitHeight), ListViewHeader (own height) property int tableRowHeight: tallRows ? diff --git a/src/EasyApp/Gui/Components/ListViewDelegate.qml b/src/EasyApp/Gui/Components/ListViewDelegate.qml index 396fd8b1..93808b9b 100644 --- a/src/EasyApp/Gui/Components/ListViewDelegate.qml +++ b/src/EasyApp/Gui/Components/ListViewDelegate.qml @@ -102,7 +102,7 @@ Rectangle { color: control.inSelection && !editing ? EaStyle.Colors.themeRowHighlightHovered : EaStyle.Colors.themeRowHovered - opacity: (listView && listView.hoveredIndex === index) || editing ? 1 : 0 + opacity: mouseHoverHandler.hovered || editing ? 1 : 0 Behavior on opacity { NumberAnimation { duration: EaStyle.Sizes.tableHighlightMoveDuration } } Behavior on color { EaAnimations.ThemeChange {} } } @@ -148,12 +148,5 @@ Rectangle { acceptedDevices: PointerDevice.AllDevices cursorShape: Qt.PointingHandCursor blocking: false - onHoveredChanged: { - if (index < 0) return - if (hovered) - listView.hoveredIndex = index - else if (listView.hoveredIndex === index) - listView.hoveredIndex = -1 - } } } diff --git a/src/EasyApp/Gui/Components/qmldir b/src/EasyApp/Gui/Components/qmldir index 9c5960d8..b2efd937 100644 --- a/src/EasyApp/Gui/Components/qmldir +++ b/src/EasyApp/Gui/Components/qmldir @@ -24,6 +24,7 @@ ProjectDescriptionDialog 1.0 ProjectDescriptionDialog.qml ListView 1.0 ListView.qml ListViewDelegate 1.0 ListViewDelegate.qml ListViewHeader 1.0 ListViewHeader.qml +ListViewTextInput 1.0 ListViewTextInput.qml TableView 1.0 TableView.qml TableViewHeader 1.0 TableViewHeader.qml TableViewDelegate 1.0 TableViewDelegate.qml @@ -36,7 +37,6 @@ TableViewComboBox 1.0 TableViewComboBox.qml TableViewButton 1.0 TableViewButton.qml TableViewLabelControl 1.0 TableViewLabelControl.qml TableViewTextInput 1.0 TableViewTextInput.qml -ListViewTextInput 1.0 ListViewTextInput.qml JsonListModel 1.0 JsonListModel.qml diff --git a/src/EasyApp/Gui/Style/Colors.qml b/src/EasyApp/Gui/Style/Colors.qml index 32da6498..5d95437f 100644 --- a/src/EasyApp/Gui/Style/Colors.qml +++ b/src/EasyApp/Gui/Style/Colors.qml @@ -58,7 +58,6 @@ QtObject { property color themeRowHighlight: isDarkPalette ? "#3A484F" : "#DCF0FA" property color themeRowHighlightHovered: isDarkPalette ? "#3E5059" : "#C9E6F5" - // Application window property color appBorder: isDarkPalette ? "#292929" : "#ddd"