Skip to content

Commit 409c7ec

Browse files
authored
Merge pull request #2581 from alicevision/dev/SearchBar
[ui] Improve Search Bar component
2 parents 2a27f4c + c2c32c8 commit 409c7ec

File tree

4 files changed

+141
-55
lines changed

4 files changed

+141
-55
lines changed

meshroom/ui/qml/Controls/SearchBar.qml

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,24 @@ import MaterialIcons 2.2
99
*/
1010

1111
FocusScope {
12+
id: root
1213
property alias textField: field
1314
property alias text: field.text
1415

16+
// Enables hiding and showing of the text field on Search button click
17+
property bool toggle: false
18+
property bool isVisible: false
19+
20+
// Size properties
21+
property int maxWidth: 150
22+
property int minWidth: 30
23+
24+
// The default width is computed based on whether toggling is enabled and if the visibility is true
25+
width: toggle && isVisible ? maxWidth : minWidth
26+
27+
// Keyboard interaction related signals
28+
signal accepted()
29+
1530
implicitHeight: childrenRect.height
1631
Keys.forwardTo: [field]
1732

@@ -24,10 +39,17 @@ FocusScope {
2439
}
2540

2641
RowLayout {
42+
spacing: 0
2743
width: parent.width
2844

29-
MaterialLabel {
45+
MaterialToolButton {
3046
text: MaterialIcons.search
47+
48+
onClicked: {
49+
isVisible = !root.isVisible
50+
// Set Focus on the Text Field
51+
field.focus = field.visible
52+
}
3153
}
3254

3355
TextField {
@@ -36,10 +58,46 @@ FocusScope {
3658
Layout.fillWidth: true
3759
selectByMouse: true
3860

61+
rightPadding: clear.width
62+
63+
// The text field is visible either when toggle is not activated or the visible property is set
64+
visible: root.toggle ? root.isVisible : true
65+
3966
// Ensure the field has focus when the text is modified
4067
onTextChanged: {
4168
forceActiveFocus()
4269
}
70+
71+
// Handle enter Key press and forward it to the parent
72+
Keys.onPressed: (event)=> {
73+
if ((event.key == Qt.Key_Return || event.key == Qt.Key_Enter)) {
74+
event.accepted = true
75+
root.accepted()
76+
}
77+
}
78+
79+
MaterialToolButton {
80+
id: clear
81+
82+
// Anchors
83+
anchors.right: parent.right
84+
anchors.rightMargin: 2 // Leave a tiny bit of space so that its highlight does not overlap with the boundary of the parent
85+
anchors.verticalCenter: parent.verticalCenter
86+
87+
// Style
88+
font.pointSize: 8
89+
text: MaterialIcons.close
90+
ToolTip.text: "Clears text."
91+
92+
// States
93+
visible: field.text
94+
95+
// Signals -> Slots
96+
onClicked: {
97+
field.text = ""
98+
parent.focus = true
99+
}
100+
}
43101
}
44102
}
45103
}

meshroom/ui/qml/GraphEditor/GraphEditor.qml

Lines changed: 76 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,11 @@ Item {
10051005
padding: 2
10061006
anchors.bottom: parent.bottom
10071007
RowLayout {
1008+
id: navigation
10081009
spacing: 4
1010+
1011+
// Default index for search
1012+
property int currentIndex: -1
10091013
// Fit
10101014
MaterialToolButton {
10111015
text: MaterialIcons.fullscreen
@@ -1044,7 +1048,7 @@ Item {
10441048
ComboBox {
10451049
flat: true
10461050
model: ['Minimum', 'Maximum']
1047-
implicitWidth: 80
1051+
implicitWidth: 85
10481052
currentIndex: uigraph ? uigraph.layout.depthMode : -1
10491053
onActivated: {
10501054
uigraph.layout.depthMode = currentIndex
@@ -1071,50 +1075,44 @@ Item {
10711075
uigraph.setSelectedNodesColor(color)
10721076
}
10731077
}
1074-
}
1075-
}
10761078

1077-
// Graph Nodes Search
1078-
FloatingPane {
1079-
id: navigation
1080-
padding: 2
1081-
anchors.top: parent.top
1082-
1083-
property int currentIndex: -1
1084-
1085-
RowLayout {
1086-
spacing: 0
1079+
// Separator
1080+
Rectangle {
1081+
Layout.fillHeight: true
1082+
Layout.margins: 2
1083+
implicitWidth: 1
1084+
color: activePalette.window
1085+
}
10871086

1087+
// Search nodes in the graph
10881088
SearchBar {
10891089
id: graphSearchBar
1090-
Layout.minimumWidth: 150
1091-
width: 150
1090+
1091+
toggle: true // enable toggling the actual text field by the search button
1092+
Layout.minimumWidth: graphSearchBar.width
1093+
maxWidth: 150
1094+
10921095
textField.background.opacity: 0.5
10931096
textField.onTextChanged: navigation.currentIndex = -1
1097+
1098+
onAccepted: {
1099+
navigation.navigateForward()
1100+
}
10941101
}
10951102

10961103
MaterialToolButton {
10971104
text: MaterialIcons.arrow_left
10981105
padding: 0
1099-
enabled: graphSearchBar.text !== ""
1100-
onClicked: {
1101-
navigation.currentIndex--
1102-
if (navigation.currentIndex === -1)
1103-
navigation.currentIndex = filteredNodes.count - 1
1104-
navigation.nextItem()
1105-
}
1106+
visible: graphSearchBar.text !== ""
1107+
onClicked: navigation.navigateBackward()
11061108
}
11071109

11081110
MaterialToolButton {
1111+
id: nextArrow
11091112
text: MaterialIcons.arrow_right
11101113
padding: 0
1111-
enabled: graphSearchBar.text !== ""
1112-
onClicked: {
1113-
navigation.currentIndex++
1114-
if (navigation.currentIndex === filteredNodes.count)
1115-
navigation.currentIndex = 0
1116-
navigation.nextItem()
1117-
}
1114+
visible: graphSearchBar.text !== ""
1115+
onClicked: navigation.navigateForward()
11181116
}
11191117

11201118
Label {
@@ -1124,32 +1122,58 @@ Item {
11241122
visible: graphSearchBar.text !== ""
11251123
}
11261124

1127-
}
1128-
1129-
Repeater {
1130-
id: filteredNodes
1131-
model: SortFilterDelegateModel {
1132-
model: root.graph ? root.graph.nodes : undefined
1133-
sortRole: "label"
1134-
filters: [{role: "label", value: graphSearchBar.text}]
1135-
delegate: Item {
1136-
property var index_: index
1137-
}
1138-
function modelData(item, roleName_) {
1139-
return item.model.object[roleName_]
1125+
Repeater {
1126+
id: filteredNodes
1127+
model: SortFilterDelegateModel {
1128+
model: root.graph ? root.graph.nodes : undefined
1129+
sortRole: "label"
1130+
filters: [{role: "label", value: graphSearchBar.text}]
1131+
delegate: Item {
1132+
visible: false // Hide the items to not affect the layout as the nodes model gets changes
1133+
property var index_: index
1134+
}
1135+
function modelData(item, roleName_) {
1136+
return item.model.object[roleName_]
1137+
}
11401138
}
11411139
}
1142-
}
11431140

1144-
function nextItem() {
1145-
// Compute bounding box
1146-
var node = nodeRepeater.itemAt(filteredNodes.itemAt(navigation.currentIndex).index_)
1147-
var bbox = Qt.rect(node.x, node.y, node.width, node.height)
1148-
// Rescale to fit the bounding box in the view, zoom is limited to prevent huge text
1149-
draggable.scale = Math.min(Math.min(root.width / bbox.width, root.height / bbox.height),maxZoom)
1150-
// Recenter
1151-
draggable.x = bbox.x*draggable.scale * -1 + (root.width - bbox.width * draggable.scale) * 0.5
1152-
draggable.y = bbox.y*draggable.scale * -1 + (root.height - bbox.height * draggable.scale) * 0.5
1141+
function navigateForward() {
1142+
/**
1143+
* Moves the navigation index forwards and focuses on the next node as per index.
1144+
*/
1145+
if (!filteredNodes.count)
1146+
return
1147+
1148+
navigation.currentIndex++
1149+
if (navigation.currentIndex === filteredNodes.count)
1150+
navigation.currentIndex = 0
1151+
navigation.nextItem()
1152+
}
1153+
1154+
function navigateBackward() {
1155+
/**
1156+
* Moves the navigation index backwards and focuses on the previous node as per index.
1157+
*/
1158+
if (!filteredNodes.count)
1159+
return
1160+
1161+
navigation.currentIndex--
1162+
if (navigation.currentIndex === -1)
1163+
navigation.currentIndex = filteredNodes.count - 1
1164+
navigation.nextItem()
1165+
}
1166+
1167+
function nextItem() {
1168+
// Compute bounding box
1169+
var node = nodeRepeater.itemAt(filteredNodes.itemAt(navigation.currentIndex).index_)
1170+
var bbox = Qt.rect(node.x, node.y, node.width, node.height)
1171+
// Rescale to fit the bounding box in the view, zoom is limited to prevent huge text
1172+
draggable.scale = Math.min(Math.min(root.width / bbox.width, root.height / bbox.height),maxZoom)
1173+
// Recenter
1174+
draggable.x = bbox.x*draggable.scale * -1 + (root.width - bbox.width * draggable.scale) * 0.5
1175+
draggable.y = bbox.y*draggable.scale * -1 + (root.height - bbox.height * draggable.scale) * 0.5
1176+
}
11531177
}
11541178
}
11551179

meshroom/ui/qml/GraphEditor/NodeEditor.qml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ Panel {
104104

105105
SearchBar {
106106
id: searchBar
107-
width: 150
107+
toggle: true // Enable toggling the actual text field by the search button
108+
Layout.minimumWidth: searchBar.width
109+
maxWidth: 150
108110
enabled: tabBar.currentIndex === 0 || tabBar.currentIndex === 5
109111
}
110112

meshroom/ui/qml/ImageGallery/ImageGallery.qml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ Panel {
121121
headerBar: RowLayout {
122122
SearchBar {
123123
id: searchBar
124-
width: 150
124+
toggle: true // Enable toggling the actual text field by the search button
125+
Layout.minimumWidth: searchBar.width
126+
maxWidth: 150
125127
}
126128

127129
MaterialToolButton {

0 commit comments

Comments
 (0)