Skip to content

Commit

Permalink
Widgets|libgui: Making selections using BrowserWidget
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed Sep 1, 2019
1 parent da74ede commit ed4552d
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 90 deletions.
6 changes: 5 additions & 1 deletion doomsday/libs/gui/include/de/gui/directorytreedata.h
Expand Up @@ -28,6 +28,9 @@ class DirectoryItem;

class LIBGUI_PUBLIC DirectoryTreeData : public ui::TreeData
{
public:
DE_ERROR(InvalidDirectoryError);

public:
DirectoryTreeData();

Expand All @@ -36,6 +39,7 @@ class LIBGUI_PUBLIC DirectoryTreeData : public ui::TreeData

// Implements ui::TreeData.
bool contains(const Path &path) const override;
ui::Data & items(const Path &path) override;
const ui::Data &items(const Path &path) const override;

private:
Expand All @@ -60,7 +64,7 @@ class LIBGUI_PUBLIC DirectoryItem : public ui::Item
File::Status status() const { return _status; }
Path path() const { return _directory / name(); }

bool isDirectory() const
inline bool isDirectory() const
{
return status().type() == File::Type::Folder;
}
Expand Down
1 change: 1 addition & 0 deletions doomsday/libs/gui/include/de/gui/treedata.h
Expand Up @@ -35,6 +35,7 @@ class LIBGUI_PUBLIC TreeData
virtual ~TreeData() = default;

virtual bool contains(const Path &path) const = 0;
virtual Data & items(const Path &path) = 0;
virtual const Data &items(const Path &path) const = 0;

DE_CAST_METHODS()
Expand Down
8 changes: 7 additions & 1 deletion doomsday/libs/gui/include/de/widgets/browserwidget.h
Expand Up @@ -33,12 +33,18 @@ class LIBGUI_PUBLIC BrowserWidget : public GuiWidget
public:
BrowserWidget(const String &name = {});

void setData(const ui::TreeData &data, int averageItemHeight);
void setEmptyContentText(const String &text);

void setData(ui::TreeData &data, int averageItemHeight);
const ui::TreeData &data() const;

void setSelected(const ui::Item &item);
List<const ui::Item *> selected() const;

MenuWidget &menu();

void setCurrentPath(const Path &path);
Path currentPath() const;

DE_AUDIENCE(Navigation, void browserNavigatedTo(BrowserWidget &, const Path &))

Expand Down
Expand Up @@ -19,6 +19,7 @@
#pragma once

#include "../BrowserWidget"
#include "../DirectoryTreeData"

namespace de {

Expand All @@ -33,7 +34,10 @@ class DirectoryBrowserWidget : public BrowserWidget
public:
DirectoryBrowserWidget(Flags flags = ShowFiles, const String &name = "dirbrowser");

DE_AUDIENCE(Selection, void pathSelected(DirectoryBrowserWidget &, const Path &))
NativePath currentDirectory() const;
List<NativePath> selectedPaths() const;

DE_AUDIENCE(Selection, void itemSelected(DirectoryBrowserWidget &, const DirectoryItem &))

private:
DE_PRIVATE(d)
Expand Down
108 changes: 28 additions & 80 deletions doomsday/libs/gui/src/dialogs/filedialog_x11.cpp
Expand Up @@ -23,6 +23,7 @@
namespace de {

DE_PIMPL_NOREF(FileDialog)
, DE_OBSERVES(DirectoryBrowserWidget, Selection)
{
String title = "Select File";
String prompt = "OK";
Expand Down Expand Up @@ -62,13 +63,29 @@ DE_PIMPL_NOREF(FileDialog)

browser = new DirectoryBrowserWidget(
(behavior & AcceptFiles ? DirectoryBrowserWidget::ShowFiles : 0));
if (behavior.testFlag(AcceptDirectories))
{
browser->setEmptyContentText("No Subdirs");
}
browser->setCurrentPath(initialLocation);
browser->rule().setInput(Rule::Height, browser->rule().width());
browser->audienceForSelection() += this;
dlg->area().add(browser);
dlg->updateLayout();

return dlg;
}

void itemSelected(DirectoryBrowserWidget &, const DirectoryItem &item) override
{
if (behavior.testFlag(AcceptFiles))
{
if (!item.isSelected())
{
browser->setSelected(item);
}
}
}
};

FileDialog::FileDialog() : d(new Impl)
Expand Down Expand Up @@ -120,90 +137,21 @@ bool FileDialog::exec(GuiRootWidget &root)
{
d->selection.clear();

// IFileOpenDialog *dlg = nullptr;
// if (FAILED(CoCreateInstance(
// CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dlg))))
// {
// return false;
// }

// // Configure the dialog according to user-specified options.
// DWORD options;
// dlg->GetOptions(&options);
// options |= FOS_FORCEFILESYSTEM;
// if (d->behavior & MultipleSelection)
// {
// options |= FOS_ALLOWMULTISELECT;
// }
// if (d->behavior & AcceptFiles)
// {
// options |= FOS_FILEMUSTEXIST;
// }
// if (d->behavior & AcceptDirectories)
// {
// options |= FOS_PICKFOLDERS | FOS_PATHMUSTEXIST;
// }
// dlg->SetOptions(options);
// {
// const auto path = d->initialLocation.toString().toUtf16();
// IShellItem *folder = nullptr;
// if (SUCCEEDED(SHCreateItemFromParsingName(path.c_wstr(), nullptr, IID_PPV_ARGS(&folder))))
// {
// dlg->SetDefaultFolder(folder);
// folder->Release();
// }
// }
// dlg->SetTitle(d->title.toUtf16().c_wstr());
// dlg->SetOkButtonLabel(d->prompt.toUtf16().c_wstr());
// if (d->fileTypes)
// {
// List<COMDLG_FILTERSPEC> filters;
// const auto strings = d->filters();
// for (const auto &str : strings)
// {
// COMDLG_FILTERSPEC spec;
// spec.pszName = str.first.c_wstr();
// spec.pszSpec = str.second.c_wstr();
// filters << spec;
// }
// dlg->SetFileTypes(UINT(filters.size()), filters.data());
// }

// if (SUCCEEDED(dlg->Show(nullptr)))
// {
// IShellItemArray *results = nullptr;
// dlg->GetResults(&results);
// if (results)
// {
// DWORD resultCount = 0;
// results->GetCount(&resultCount);
// for (unsigned i = 0; i < resultCount; ++i)
// {
// IShellItem *result = nullptr;
// if (SUCCEEDED(results->GetItemAt(i, &result)))
// {
// PWSTR itemPath;
// if (SUCCEEDED(result->GetDisplayName(SIGDN_FILESYSPATH, &itemPath)))
// {
// d->selection << NativePath(
// String::fromUtf16(Block(itemPath, 2 * (wcslen(itemPath) + 1))));
// CoTaskMemFree(itemPath);
// }
// result->Release();
// }
// }
// results->Release();
// }
// }

// // Cleanup.
// dlg->Release();

auto *dlg = d->makeDialog();
if (dlg->exec(root))
{
// Get the selected items.

if (d->behavior & AcceptDirectories)
{
d->selection << d->browser->currentDirectory();
}
else
{
for (auto path : d->browser->selectedPaths())
{
d->selection << path;
}
}
}

return !d->selection.empty();
Expand Down
11 changes: 10 additions & 1 deletion doomsday/libs/gui/src/directorytreedata.cpp
Expand Up @@ -92,8 +92,17 @@ bool DirectoryTreeData::contains(const Path &path) const

const ui::Data &DirectoryTreeData::items(const Path &path) const
{
DE_ASSERT(contains(path));
const NativePath dir(path);
if (d->pathItems.contains(dir))
{
return *d->pathItems[dir];
}
throw InvalidDirectoryError("DirectoryTreeData::items", "Not found: " + path.asText());
}

ui::Data &DirectoryTreeData::items(const Path &path)
{
DE_ASSERT(contains(path));
const NativePath dir(path);
if (!d->pathItems.contains(dir))
{
Expand Down
53 changes: 50 additions & 3 deletions doomsday/libs/gui/src/widgets/browserwidget.cpp
Expand Up @@ -34,7 +34,8 @@ DE_GUI_PIMPL(BrowserWidget)
int scrollPosY;
};

const ui::TreeData *data = nullptr;
ui::TreeData *data = nullptr;
List<ui::DataPos> selectedItems;
Path path;
Map<Path, SavedState> savedState;
LabelWidget *noContents;
Expand Down Expand Up @@ -110,6 +111,7 @@ DE_GUI_PIMPL(BrowserWidget)

DE_ASSERT(data);

clearSelection();
savedState[path].scrollPosY = scroller->scrollPosition().y;

// TODO: This is an async op, need to show progress widget.
Expand Down Expand Up @@ -155,7 +157,8 @@ DE_GUI_PIMPL(BrowserWidget)
auto &button = self().addNew<ButtonWidget>();
button.setSizePolicy(ui::Expand, ui::Expand);
button.setMaximumTextWidth(*contentWidth);
button.setText(Stringf("%s/", String(segment).c_str()));
const String segStr = segment.toCString();
button.setText(segStr.isEmpty() ? DE_STR("/") : segStr);

if (i == path.segmentCount() - 1)
{
Expand Down Expand Up @@ -185,6 +188,22 @@ DE_GUI_PIMPL(BrowserWidget)
}
}

void clearSelection()
{
if (!menu->items().isEmpty())
{
auto &items = data->items(path);
for (const auto pos : selectedItems)
{
if (pos < items.size())
{
items.at(pos).setSelected(false);
}
}
}
selectedItems.clear();
}

DE_PIMPL_AUDIENCES(Navigation)
};

Expand All @@ -195,7 +214,12 @@ BrowserWidget::BrowserWidget(const String &name)
, d(new Impl(this))
{}

void BrowserWidget::setData(const ui::TreeData &data, int averageItemHeight)
void BrowserWidget::setEmptyContentText(const String &text)
{
d->noContents->setText(text);
}

void BrowserWidget::setData(ui::TreeData &data, int averageItemHeight)
{
d->data = &data;
d->menu->organizer().setRecyclingEnabled(true);
Expand All @@ -217,4 +241,27 @@ void BrowserWidget::setCurrentPath(const Path &path)
d->changeTo(path, true);
}

Path BrowserWidget::currentPath() const
{
return d->path;
}

void BrowserWidget::setSelected(const ui::Item &item)
{
d->clearSelection();

auto &items = d->data->items(d->path);
const ui::DataPos pos = items.find(item);
DE_ASSERT(pos != ui::Data::InvalidPos);
d->selectedItems.push_back(pos);
items.at(pos).setSelected(true);
}

List<const ui::Item *> BrowserWidget::selected() const
{
return map<List<const ui::Item *>>(d->selectedItems, [this](ui::DataPos pos) {
return &d->data->items(d->path).at(pos);
});
}

} // namespace de
22 changes: 19 additions & 3 deletions doomsday/libs/gui/src/widgets/directorybrowserwidget.cpp
Expand Up @@ -27,7 +27,6 @@ DE_GUI_PIMPL(DirectoryBrowserWidget)
{
class ItemWidget : public ButtonWidget
{
// const DirectoryItem *_item{};
LabelWidget *_status;

public:
Expand All @@ -44,13 +43,15 @@ DE_GUI_PIMPL(DirectoryBrowserWidget)
.setInput(Rule::Right, rule().right())
.setInput(Rule::Top, rule().top())
.setInput(Rule::Height, height);
_status->setTextColor("altaccent");

margins().setRight(_status->rule().width());
}

void updateItem(const DirectoryItem &dirItem)
{
setColorTheme(dirItem.isSelected() ? Inverted : Normal);
_status->setTextColor(dirItem.isSelected() ? "inverted.altaccent" : "altaccent");

if (dirItem.isDirectory())
{
setText(_E(F) + dirItem.name() + _E(l) "/");
Expand Down Expand Up @@ -122,7 +123,7 @@ DE_GUI_PIMPL(DirectoryBrowserWidget)
{
DE_FOR_PUBLIC_AUDIENCE(Selection, i)
{
i->pathSelected(self(), dirItem.path());
i->itemSelected(self(), dirItem);
}
}
};
Expand All @@ -145,4 +146,19 @@ DirectoryBrowserWidget::DirectoryBrowserWidget(Flags flags, const String &name)
, d(new Impl(this, flags))
{}

NativePath DirectoryBrowserWidget::currentDirectory() const
{
return BrowserWidget::currentPath();
}

List<NativePath> DirectoryBrowserWidget::selectedPaths() const
{
List<NativePath> sel;
for (const auto *item : selected())
{
sel.push_back(item->as<DirectoryItem>().path());
}
return sel;
}

} // namespace de

0 comments on commit ed4552d

Please sign in to comment.