Skip to content

Commit f0bec56

Browse files
committed
Improve FileChooser
Added multiple selection capabilities and different selection modes for files, dirs and both.
1 parent 636a6b6 commit f0bec56

File tree

5 files changed

+294
-30
lines changed

5 files changed

+294
-30
lines changed

src/gui/FileChooser.cpp

+174-19
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,79 @@
77

88
#include <algorithm>
99

10-
FileChooser::FileChooser(std::string title, std::vector<std::string> extensions)
10+
FileChooser::FileChooser(FileChooser::Mode mode)
11+
: _mode(mode)
12+
{
13+
}
14+
15+
FileChooser::FileChooser(std::string title, const std::string& initialDirectory, Mode mode)
1116
: _title(std::move(title))
12-
, _extensions(std::move(extensions))
17+
, _mode(mode)
18+
{
19+
if (!initialDirectory.empty())
20+
{
21+
ChangeDirectory(initialDirectory);
22+
}
23+
}
24+
25+
void FileChooser::Title(const std::string& title)
1326
{
27+
_title = title;
28+
}
29+
30+
void FileChooser::CurrentDirectory(const std::string& path)
31+
{
32+
if (path.empty())
33+
{
34+
return;
35+
}
36+
37+
ChangeDirectory(path);
38+
}
39+
40+
void FileChooser::Context(const std::string& context)
41+
{
42+
_context = context;
43+
}
44+
45+
const std::string& FileChooser::Context() const
46+
{
47+
return _context;
48+
}
49+
50+
void FileChooser::AllowedExtensions(std::vector<std::string> extensions)
51+
{
52+
_extensions = std::move(extensions);
53+
}
54+
55+
const std::vector<std::string>& FileChooser::AllowedExtensions() const
56+
{
57+
return _extensions;
58+
}
59+
60+
61+
void FileChooser::MultiSelect(bool enabled)
62+
{
63+
_multiSelect = enabled;
64+
}
65+
66+
bool FileChooser::MultiSelect() const
67+
{
68+
return _multiSelect;
1469
}
1570

1671
void FileChooser::Show()
1772
{
73+
_selectedFiles.clear();
1874
_visible = true;
1975
}
2076

77+
void FileChooser::Close()
78+
{
79+
ImGui::CloseCurrentPopup();
80+
_visible = false;
81+
}
82+
2183
bool FileChooser::Draw()
2284
{
2385
if (!_visible)
@@ -32,7 +94,9 @@ bool FileChooser::Draw()
3294
ChangeDirectory(Poco::Path::home());
3395
}
3496

35-
if (ImGui::Begin(_title.c_str(), &_visible), ImGuiWindowFlags_NoCollapse)
97+
ImGui::OpenPopup(_title.c_str());
98+
99+
if (ImGui::BeginPopupModal(_title.c_str(), &_visible, ImGuiWindowFlags_NoCollapse))
36100
{
37101
DrawNavButtons();
38102

@@ -41,12 +105,14 @@ bool FileChooser::Draw()
41105
char pathBuffer[2048]{};
42106
strncpy(pathBuffer, _currentDir.toString().c_str(), std::min<size_t>(2047, _currentDir.toString().size()));
43107

108+
ImGui::SetNextItemWidth(-1);
109+
44110
if (ImGui::InputText("##path", &pathBuffer[0], IM_ARRAYSIZE(pathBuffer)), ImGuiInputTextFlags_EnterReturnsTrue)
45111
{
46112
ChangeDirectory(std::string(pathBuffer));
47113
}
48114

49-
if (ImGui::BeginListBox("##filelist", ImVec2(-1, -1)))
115+
if (ImGui::BeginListBox("##filelist", ImVec2(-1, -ImGui::GetTextLineHeight() - ImGui::GetStyle().FramePadding.y * 2 - 4)))
50116
{
51117
if (_currentDir.toString().empty())
52118
{
@@ -71,26 +137,56 @@ bool FileChooser::Draw()
71137
}
72138

73139
ImGui::EndListBox();
140+
141+
ImGui::PushStyleColor(ImGuiCol_Button, 0xFF000080);
142+
if (ImGui::Button("Cancel"))
143+
{
144+
_selectedFiles.clear();
145+
fileSelected = true;
146+
Close();
147+
}
148+
ImGui::PopStyleColor();
149+
ImGui::SameLine();
150+
if (ImGui::Button("Select"))
151+
{
152+
for (auto index : _selectedFileIndices)
153+
{
154+
_selectedFiles.emplace_back(_currentFileList.at(index));
155+
}
156+
157+
if (_selectedFileIndices.empty() && _mode == Mode::Directory)
158+
{
159+
_selectedFiles.emplace_back(Poco::Path(_currentDir).makeDirectory());
160+
}
161+
else
162+
{
163+
// ToDo: Display "Select at least one entry from the list"
164+
}
165+
166+
fileSelected = true;
167+
Close();
168+
}
169+
170+
ImGui::EndPopup();
74171
}
75172
}
76173
else
77174
{
78-
_visible = false;
175+
Close();
79176
}
80177

81-
ImGui::End();
82178

83179
return fileSelected;
84180
}
85181

86-
const Poco::File& FileChooser::SelectedFile() const
182+
const std::vector<Poco::File>& FileChooser::SelectedFiles() const
87183
{
88-
return _selectedFile;
184+
return _selectedFiles;
89185
}
90186

91187
void FileChooser::DrawNavButtons()
92188
{
93-
ImGui::Checkbox("Show hidden files", &_showhidden);
189+
ImGui::Checkbox("Show hidden", &_showHidden);
94190

95191
// Root path buttons first
96192
std::vector<std::string> roots;
@@ -131,7 +227,7 @@ bool FileChooser::PopulateFileList()
131227
int index = 0;
132228
for (const auto& file : _currentFileList)
133229
{
134-
bool isSelected = _selectedFileIndex == index;
230+
bool isSelected = (_selectedFileIndices.find(index) != _selectedFileIndices.end());
135231
bool isDirectory = false;
136232
try
137233
{
@@ -152,10 +248,11 @@ bool FileChooser::PopulateFileList()
152248

153249
if (ImGui::Selectable(filename.c_str(), isSelected, ImGuiSelectableFlags_AllowDoubleClick))
154250
{
155-
_selectedFileIndex = index;
251+
UpdateListSelection(index, isSelected);
156252

157253
if (ImGui::IsMouseDoubleClicked(0))
158254
{
255+
_selectedFiles.clear();
159256
if (isDirectory)
160257
{
161258
newDir = filePath;
@@ -164,8 +261,8 @@ bool FileChooser::PopulateFileList()
164261
}
165262
else
166263
{
167-
_selectedFile = filePath;
168-
poco_debug_f1(_logger, "User selected file: %s", _selectedFile.path());
264+
_selectedFiles.emplace_back(filePath);
265+
poco_debug_f1(_logger, "User selected file: %s", filePath.toString());
169266
fileSelected = true;
170267
_visible = false;
171268
}
@@ -183,12 +280,22 @@ bool FileChooser::PopulateFileList()
183280
return fileSelected;
184281
}
185282

186-
void FileChooser::ChangeDirectory(const Poco::Path& newDirectory)
283+
void FileChooser::ChangeDirectory(Poco::Path newDirectory)
187284
{
285+
newDirectory.makeDirectory();
286+
287+
if (_currentDir.toString() == newDirectory.toString())
288+
{
289+
return;
290+
}
291+
188292
_currentDir = newDirectory;
189-
_currentDir.makeDirectory();
190293

191294
_currentFileList.clear();
295+
_selectedFileIndices.clear();
296+
_selectedFileIndex = -1;
297+
298+
poco_information_f1(_logger, "Changing dir: %s", newDirectory.toString());
192299

193300
if (_currentDir.toString().empty())
194301
{
@@ -219,13 +326,13 @@ void FileChooser::ChangeDirectory(const Poco::Path& newDirectory)
219326
{
220327
}
221328

222-
if (!isDirectory && (!isHidden || _showhidden))
329+
if ((!isHidden || _showHidden))
223330
{
224-
if (_extensions.empty())
331+
if ((_mode != Mode::Directory && _extensions.empty()) || isDirectory)
225332
{
226333
_currentFileList.push_back(*directoryIterator);
227334
}
228-
else
335+
else if (_mode != Mode::Directory)
229336
{
230337
auto fileExtension = directoryIterator.path().getExtension();
231338
for (const auto& extension : _extensions)
@@ -240,4 +347,52 @@ void FileChooser::ChangeDirectory(const Poco::Path& newDirectory)
240347

241348
++directoryIterator;
242349
}
243-
}
350+
}
351+
352+
void FileChooser::UpdateListSelection(int index, bool isSelected)
353+
{
354+
// Reset selection on simple click
355+
if (!ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && !ImGui::IsKeyDown(ImGuiKey_RightCtrl) && !ImGui::IsKeyDown(ImGuiKey_LeftShift) && !ImGui::IsKeyDown(ImGuiKey_RightShift))
356+
{
357+
_selectedFileIndices.clear();
358+
_selectedFileIndices.insert(index);
359+
}
360+
361+
// Multiple selection on shift+click
362+
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift) && _selectedFileIndex >= 0)
363+
{
364+
if (!ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && !ImGui::IsKeyDown(ImGuiKey_RightCtrl))
365+
{
366+
// Replace selection if ctrl is not pressed
367+
_selectedFileIndices.clear();
368+
}
369+
370+
if (!_multiSelect)
371+
{
372+
_selectedFileIndex = index;
373+
}
374+
375+
for (int selIndex = std::min(_selectedFileIndex, index); selIndex <= std::max(_selectedFileIndex, index); selIndex++)
376+
{
377+
_selectedFileIndices.insert(selIndex);
378+
}
379+
}
380+
// Toggle selection with ctrl+click
381+
else if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl))
382+
{
383+
if (isSelected)
384+
{
385+
_selectedFileIndices.erase(index);
386+
}
387+
else
388+
{
389+
if (!_multiSelect)
390+
{
391+
_selectedFileIndices.clear();
392+
}
393+
_selectedFileIndices.insert(index);
394+
}
395+
}
396+
397+
_selectedFileIndex = index;
398+
}

0 commit comments

Comments
 (0)