diff --git a/res/mdpi/folder.png b/res/mdpi/folder.png new file mode 100644 index 00000000..13c19624 Binary files /dev/null and b/res/mdpi/folder.png differ diff --git a/res/mdpi/text-plain.png b/res/mdpi/text-plain.png new file mode 100644 index 00000000..91b7da5c Binary files /dev/null and b/res/mdpi/text-plain.png differ diff --git a/src/dlangui/core/files.d b/src/dlangui/core/files.d index 22e8c4f4..7f64ac07 100644 --- a/src/dlangui/core/files.d +++ b/src/dlangui/core/files.d @@ -143,11 +143,70 @@ struct RootEntry { return res; } +/// returns true if directory is root directory (e.g. / or C:\) +bool isRoot(string path) { + string root = rootName(path); + if (path.equal(root)) + return true; + return false; +} + +/// Filters file name by pattern list +bool filterFilename(string filename, string[] filters) { + return true; +} + +/** List directory content + + Optionally filters file names by filter. + + Result will be placed into entries array. + + Returns true if directory exists and listed successfully, false otherwise. +*/ +bool listDirectory(string dir, bool includeDirs, bool includeFiles, string[] filters, ref DirEntry[] entries) { + entries.length = 0; + if (!isDir(dir)) { + return false; + } + if (!isRoot(dir) && includeDirs) { + entries ~= DirEntry(appendPath(dir, "..")); + } + + try { + DirEntry[] dirs; + DirEntry[] files; + foreach (DirEntry e; dirEntries(dir, SpanMode.shallow)) { + if (e.isDir) { + dirs ~= e; + } else if (e.isFile) { + files ~= e; + } + } + if (includeDirs) + foreach(DirEntry e; dirs) + entries ~= e; + if (includeFiles) + foreach(DirEntry e; files) + if (filterFilename(e.name, filters)) + entries ~= e; + return true; + } catch (FileException e) { + return false; + } + +} + /** Returns true if char ch is / or \ slash */ bool isPathDelimiter(char ch) { return ch == '/' || ch == '\\'; } +/// Returns current directory +@property string currentDir() { + return getcwd(); +} + /** Returns current executable path only, including last path delimiter - removes executable name from result of std.file.thisExePath() */ @property string exePath() { string path = thisExePath(); diff --git a/src/dlangui/dialogs/filedlg.d b/src/dlangui/dialogs/filedlg.d index 2c46e0ac..747cd407 100644 --- a/src/dlangui/dialogs/filedlg.d +++ b/src/dlangui/dialogs/filedlg.d @@ -35,6 +35,12 @@ import dlangui.widgets.grid; import dlangui.widgets.editors; import dlangui.platforms.common.platform; import dlangui.dialogs.dialog; + +private import std.file; +private import std.path; +private import std.utf; +private import std.conv : to; + /// flags for file dialog options enum FileDialogFlag : uint { @@ -49,7 +55,7 @@ enum FileDialogFlag : uint { } /// File open / save dialog -class FileDialog : Dialog { +class FileDialog : Dialog, CustomGridCellAdapter { protected EditLine path; protected EditLine filename; protected StringGridWidget list; @@ -58,15 +64,80 @@ class FileDialog : Dialog { protected VerticalLayout rightPanel; protected RootEntry[] _roots; + protected string _path; + protected string _filename; + protected DirEntry[] _entries; + protected bool _isRoot; this(UIString caption, Window parent, uint fileDialogFlags = DialogFlag.Modal | FileDialogFlag.FileMustExist) { super(caption, parent, fileDialogFlags); } - protected void rootEntrySelected(RootEntry entry) { - // TODO + protected bool openDirectory(string dir) { + list.rows = 0; + string[] filters; + if (!listDirectory(dir, true, true, filters, _entries)) + return false; + _path = dir; + _isRoot = isRoot(dir); + path.text = toUTF32(_path); + list.rows = _entries.length; + for (int i = 0; i < _entries.length; i++) { + string fname = baseName(_entries[i].name); + string sz; + string date; + bool d = _entries[i].isDir; + list.setCellText(1, i, toUTF32(fname)); + if (d) { + list.setCellText(0, i, "folder"); + } else { + list.setCellText(0, i, "text-plain"d); + sz = to!string(_entries[i].size); + date = "2014-01-01 00:00:00"; + } + list.setCellText(2, i, toUTF32(sz)); + list.setCellText(3, i, toUTF32(date)); + } + list.autoFitColumnWidths(); + return true; } + /// return true for custom drawn cell + override bool isCustomCell(int col, int row) { + if (col == 0 && row >= 0) + return true; + return false; + } + + protected DrawableRef rowIcon(int row) { + string iconId = toUTF8(list.cellText(0, row)); + DrawableRef res; + if (iconId.length) + res = drawableCache.get(iconId); + return res; + } + + /// return cell size + override Point measureCell(int col, int row) { + DrawableRef icon = rowIcon(row); + if (icon.isNull) + return Point(0, 0); + return Point(icon.width + 2, icon.height + 2); + } + + /// draw data cell content + override void drawCell(DrawBuf buf, Rect rc, int col, int row) { + DrawableRef img = rowIcon(row); + if (!img.isNull) { + Point sz; + sz.x = img.width; + sz.y = img.height; + applyAlign(rc, sz, Align.HCenter, Align.VCenter); + uint st = state; + img.drawTo(buf, rc, st); + } + } + protected Widget createRootsList() { ListWidget list = new ListWidget("ROOTS_LIST"); WidgetListAdapter adapter = new WidgetListAdapter(); @@ -76,7 +147,7 @@ class FileDialog : Dialog { btn.styleId = "TRANSPARENT_BUTTON_BACKGROUND"; btn.focusable = false; btn.onClickListener = delegate(Widget source) { - rootEntrySelected(root); + openDirectory(root.path); return true; }; adapter.widgets.add(btn); @@ -110,10 +181,11 @@ class FileDialog : Dialog { rightPanel.addChild(path); list = new StringGridWidget("files"); list.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); - list.resize(3, 3); - list.setColTitle(0, "Name"d); - list.setColTitle(1, "Size"d); - list.setColTitle(2, "Modified"d); + list.resize(4, 3); + list.setColTitle(0, " "d); + list.setColTitle(1, "Name"d); + list.setColTitle(2, "Size"d); + list.setColTitle(3, "Modified"d); list.showRowHeaders = false; list.rowSelect = true; rightPanel.addChild(list); @@ -128,7 +200,11 @@ class FileDialog : Dialog { addChild(content); addChild(createButtonsPanel([ACTION_OPEN, ACTION_CANCEL], 0, 0)); - string[] path = splitPath("/home/lve/src"); - Log.d("path: ", path); + //string[] path = splitPath("/home/lve/src"); + //Log.d("path: ", path); + + list.customCellAdapter = this; + + openDirectory(currentDir); } } diff --git a/src/dlangui/widgets/grid.d b/src/dlangui/widgets/grid.d index 56956f5d..150b250a 100644 --- a/src/dlangui/widgets/grid.d +++ b/src/dlangui/widgets/grid.d @@ -202,8 +202,25 @@ enum GridActions : int { SelectDocumentEnd, } +interface CustomGridCellAdapter { + /// return true for custom drawn cell + bool isCustomCell(int col, int row); + /// return cell size + Point measureCell(int col, int row); + /// draw data cell content + void drawCell(DrawBuf buf, Rect rc, int col, int row); +} + /// Abstract grid widget class GridWidgetBase : ScrollWidgetBase { + protected CustomGridCellAdapter _customCellAdapter; + + /// Get adapter to override drawing of some particular cells + @property CustomGridCellAdapter customCellAdapter() { return _customCellAdapter; } + /// Set adapter to override drawing of some particular cells + @property GridWidgetBase customCellAdapter(CustomGridCellAdapter adapter) { _customCellAdapter = adapter; return this; } + + /// column count (including header columns and fixed columns) protected int _cols; /// row count (including header rows and fixed rows) @@ -1140,6 +1157,9 @@ class StringGridWidget : StringGridWidgetBase { } protected override Point measureCell(int x, int y) { + if (_customCellAdapter && _customCellAdapter.isCustomCell(x, y)) { + return _customCellAdapter.measureCell(x, y); + } //Log.d("measureCell ", x, ", ", y); FontRef fnt = font; dstring txt; @@ -1158,6 +1178,9 @@ class StringGridWidget : StringGridWidgetBase { /// draw cell content protected override void drawCell(DrawBuf buf, Rect rc, int col, int row) { + if (_customCellAdapter && _customCellAdapter.isCustomCell(col, row)) { + return _customCellAdapter.drawCell(buf, rc, col, row); + } rc.shrink(2, 1); FontRef fnt = font; dstring txt = cellText(col, row);