1+ import QtQml
2+ import QtQml.Models
3+ import QtQuick
4+ import QtQuick.Controls
5+ import QtQuick.Layouts
6+ import QtQuick.Controls.impl
7+
8+ import SVSCraft
9+ import SVSCraft.UIComponents
10+ import SVSCraft.UIComponents.impl
11+
12+ import DiffScope.UIShell
13+
14+ Item {
15+ id: view
16+
17+ enum DisplayMode {
18+ Grid,
19+ List
20+ }
21+
22+ property string searchText: " "
23+ property var filesModel: null
24+ property int displayMode: FileView .List
25+ property bool newFileActionEnabled: false
26+ property string emptyTip: " "
27+
28+ readonly property var newFilePseudoElement: ({
29+ name: qsTr (" New project" ),
30+ path: " " ,
31+ lastModifiedText: " " ,
32+ thumbnail: " " ,
33+ icon: " " ,
34+ })
35+
36+ signal newFileRequested ()
37+ signal openFileRequested (int index)
38+ signal contextMenuRequested (int index)
39+
40+ component CellButton: Button {
41+ id: cell
42+ required property int index
43+ required property var modelData
44+ flat: true
45+ padding: 4
46+ Accessible .name : modelData .name + " \n " + modelData .lastModifiedText
47+ Accessible .description : modelData .path
48+ DescriptiveText .toolTip : modelData .path
49+ DescriptiveText .activated : ! modelData .newFile && hovered
50+ contentItem: ColumnLayout {
51+ id: cellContent
52+ spacing: 4
53+ Item {
54+ implicitWidth: 160
55+ implicitHeight: 120
56+ Layout .alignment : Qt .AlignHCenter
57+ Rectangle {
58+ anchors .fill : parent
59+ color: Theme .backgroundTertiaryColor
60+ ColorImage {
61+ visible: cell .index === - 1
62+ width: 80
63+ height: 80
64+ anchors .centerIn : parent
65+ source: " image://fluent-system-icons/document_add?size=48&style=regular"
66+ sourceSize .width : 80
67+ sourceSize .height : 80
68+ color: Theme .foregroundSecondaryColor
69+ }
70+ // fallback display icon as thumbnail
71+ ColorImage {
72+ width: 80
73+ height: 80
74+ anchors .centerIn : parent
75+ source: cell .modelData .icon
76+ color: cell .modelData .colorize ? Theme .foregroundSecondaryColor : " transparent"
77+ sourceSize .width : 80
78+ sourceSize .height : 80
79+ }
80+ }
81+ Image {
82+ anchors .fill : parent
83+ fillMode: Image .PreserveAspectCrop
84+ source: cell .modelData .thumbnail
85+ cache: false
86+ mipmap: true
87+ }
88+ Rectangle {
89+ anchors .fill : parent
90+ color: " transparent"
91+ border .width : 1
92+ border .color : Theme .borderColor
93+ }
94+ }
95+ Label {
96+ id: nameLabel
97+ Layout .alignment : Qt .AlignHCenter
98+ Layout .maximumWidth : 160
99+ text: cell .modelData .name
100+ elide: Text .ElideMiddle
101+ }
102+ Label {
103+ id: lastModifiedTextLabel
104+ Layout .alignment : Qt .AlignHCenter
105+ Layout .maximumWidth : 160
106+ text: cell .modelData .lastModifiedText
107+ elide: Text .ElideMiddle
108+ ThemedItem .foregroundLevel : SVS .FL_Secondary
109+ }
110+ }
111+ TapHandler {
112+ acceptedButtons: Qt .RightButton
113+ enabled: cell .index !== - 1
114+ onSingleTapped: view .contextMenuRequested (filesProxyModel .mapIndexToSource (cell .index ))
115+ }
116+ Keys .onMenuPressed : view .contextMenuRequested (filesProxyModel .mapIndexToSource (cell .index ))
117+ onClicked : () => {
118+ if (cell .index === - 1 ) {
119+ view .newFileRequested ()
120+ } else {
121+ view .openFileRequested (filesProxyModel .mapIndexToSource (cell .index ))
122+ }
123+ }
124+ }
125+ component ListItemButton: Button {
126+ id: cell
127+ required property int index
128+ required property var modelData
129+ property bool recovery: false
130+ flat: true
131+ padding: 4
132+ Accessible .name : modelData .name + " \n " + modelData .lastModifiedText
133+ Accessible .description : modelData .path
134+ DescriptiveText .toolTip : modelData .path
135+ DescriptiveText .activated : ! modelData .newFile && hovered
136+ contentItem: RowLayout {
137+ id: cellContent
138+ spacing: 4
139+ Item {
140+ implicitWidth: 48
141+ implicitHeight: 48
142+ Layout .alignment : Qt .AlignHCenter
143+ ColorImage {
144+ visible: cell .index === - 1
145+ anchors .fill : parent
146+ source: " image://fluent-system-icons/document_add?size=48&style=regular"
147+ color: Theme .foregroundSecondaryColor
148+ sourceSize .width : 48
149+ sourceSize .height : 48
150+ }
151+ ColorImage {
152+ anchors .fill : parent
153+ source: cell .modelData .icon
154+ color: cell .modelData .colorize ? Theme .foregroundSecondaryColor : " transparent"
155+ sourceSize .width : 48
156+ sourceSize .height : 48
157+ }
158+ }
159+ ColumnLayout {
160+ spacing: 4
161+ Layout .fillWidth : true
162+ RowLayout {
163+ spacing: 4
164+ Label {
165+ Layout .fillWidth : true
166+ text: cell .modelData .name
167+ elide: Text .ElideMiddle
168+ }
169+ Label {
170+ text: cell .modelData .lastModifiedText
171+ elide: Text .ElideMiddle
172+ ThemedItem .foregroundLevel : SVS .FL_Secondary
173+ }
174+ }
175+ Label {
176+ visible: cell .modelData .path .length !== 0
177+ text: cell .modelData .path
178+ elide: Text .ElideMiddle
179+ ThemedItem .foregroundLevel : SVS .FL_Secondary
180+ }
181+ }
182+ }
183+ TapHandler {
184+ acceptedButtons: Qt .RightButton
185+ enabled: cell .index !== - 1
186+ onSingleTapped: view .contextMenuRequested (filesProxyModel .mapIndexToSource (cell .index ))
187+ }
188+ Keys .onMenuPressed : view .contextMenuRequested (filesProxyModel .mapIndexToSource (cell .index ))
189+ onClicked : () => {
190+ if (cell .index === - 1 ) {
191+ view .newFileRequested ()
192+ } else {
193+ view .openFileRequested (filesProxyModel .mapIndexToSource (cell .index ))
194+ }
195+ }
196+ }
197+
198+ RecentFilesProxyModel {
199+ id: filesProxyModel
200+ sourceModel: view .filesModel
201+ filterRole: USDef .RF_NameRole
202+ filterCaseSensitivity: Qt .CaseInsensitive
203+ property string _filterRegularExpression: searchTextField .text
204+ on_FilterRegularExpressionChanged: setFilterRegularExpression (_filterRegularExpression)
205+ }
206+
207+ ColumnLayout {
208+ id: recentFilesLayout
209+ spacing: 16
210+ anchors .fill : parent
211+ Item {
212+ Layout .fillWidth : true
213+ Layout .fillHeight : true
214+ visible: filesProxyModel .count === 0 && ! (view .newFileActionEnabled && view .searchText .length === 0 )
215+ Label {
216+ text: qsTr (" No result found" )
217+ ThemedItem .foregroundLevel : SVS .FL_Secondary
218+ anchors .top : parent .top
219+ anchors .left : parent .left
220+ anchors .right : parent .right
221+ horizontalAlignment: Text .AlignHCenter
222+ wrapMode: Text .Wrap
223+ visible: view .searchText .length !== 0
224+ }
225+ Label {
226+ text: view .emptyTip
227+ ThemedItem .foregroundLevel : SVS .FL_Secondary
228+ anchors .top : parent .top
229+ anchors .left : parent .left
230+ anchors .right : parent .right
231+ horizontalAlignment: Text .AlignHCenter
232+ wrapMode: Text .Wrap
233+ visible: view .searchText .length === 0
234+ }
235+ }
236+ ScrollView {
237+ id: fileGridScrollView
238+ visible: view .displayMode === FileView .Grid
239+ Layout .fillWidth : true
240+ Layout .fillHeight : true
241+ GridLayout {
242+ id: fileGridLayout
243+ rowSpacing: 16
244+ columnSpacing: (fileGridScrollView .width - 172 * columns) / (columns - 1 )
245+ width: parent .width
246+ columns: Math .floor ((fileGridScrollView .width + 8 ) / (172 + 8 ))
247+ CellButton {
248+ index: - 1
249+ modelData: view .newFilePseudoElement
250+ visible: view .searchText .length === 0 && view .newFileActionEnabled
251+ }
252+ Repeater {
253+ model: filesProxyModel
254+ CellButton {
255+ }
256+ }
257+ }
258+ }
259+ ScrollView {
260+ id: fileListScrollView
261+ visible: view .displayMode === FileView .List
262+ Layout .fillWidth : true
263+ Layout .fillHeight : true
264+ ColumnLayout {
265+ id: fileListLayout
266+ spacing: 4
267+ implicitWidth: fileListScrollView .width
268+ width: fileListScrollView .width
269+ ListItemButton {
270+ Layout .fillWidth : true
271+ index: - 1
272+ modelData: view .newFilePseudoElement
273+ visible: view .searchText .length === 0 && view .newFileActionEnabled
274+ }
275+ Repeater {
276+ model: filesProxyModel
277+ ListItemButton {
278+ Layout .fillWidth : true
279+ }
280+ }
281+ }
282+ }
283+ }
284+ }
0 commit comments