From 5fdf4ad935b31698a5faaf7abb8cceafb2065671 Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Mon, 5 Jan 2015 17:47:35 +0300 Subject: [PATCH] FileDialog - path segments --- src/dlangui/dialogs/filedlg.d | 134 +++++++++++++++++++++++++++++++--- src/dlangui/widgets/editors.d | 11 +++ src/dlangui/widgets/menu.d | 2 +- 3 files changed, 134 insertions(+), 13 deletions(-) diff --git a/src/dlangui/dialogs/filedlg.d b/src/dlangui/dialogs/filedlg.d index caba1b6a..54655f7f 100644 --- a/src/dlangui/dialogs/filedlg.d +++ b/src/dlangui/dialogs/filedlg.d @@ -33,6 +33,7 @@ import dlangui.widgets.popup; import dlangui.widgets.layouts; import dlangui.widgets.grid; import dlangui.widgets.editors; +import dlangui.widgets.menu; import dlangui.platforms.common.platform; import dlangui.dialogs.dialog; @@ -57,7 +58,7 @@ enum FileDialogFlag : uint { /// File open / save dialog class FileDialog : Dialog, CustomGridCellAdapter { - protected EditLine _edPath; + protected FilePathPanel _edPath; protected EditLine _edFilename; protected StringGridWidget _fileList; //protected StringGridWidget places; @@ -105,7 +106,7 @@ class FileDialog : Dialog, CustomGridCellAdapter { return false; _path = dir; _isRoot = isRoot(dir); - _edPath.text = toUTF32(_path); + _edPath.path = _path; //toUTF32(_path); _fileList.rows = cast(int)_entries.length; int selectionIndex = -1; for (int i = 0; i < _entries.length; i++) { @@ -247,6 +248,10 @@ class FileDialog : Dialog, CustomGridCellAdapter { return super.handleAction(action); } + bool onPathSelected(string path) { + // + return openDirectory(path, null); + } /// override to implement creation of dialog controls override void init() { @@ -272,9 +277,10 @@ class FileDialog : Dialog, CustomGridCellAdapter { content.addChild(leftPanel); content.addChild(rightPanel); - _edPath = new EditLine("path"); + _edPath = new FilePathPanel("path"); _edPath.layoutWidth(FILL_PARENT); _edPath.layoutWeight = 0; + _edPath.onPathSelectionListener = &onPathSelected; _edFilename = new EditLine("filename"); _edFilename.layoutWidth(FILL_PARENT); @@ -326,14 +332,22 @@ class FilePathPanelItem : HorizontalLayout { Listener!OnPathSelectionHandler onPathSelectionListener; this(string path) { super(null); + styleId = "LIST_ITEM"; _path = path; - string fname = baseName(path); + string fname = isRoot(path) ? path : baseName(path); _text = new TextWidget(null, toUTF32(fname)); + _text.styleId = "BUTTON_TRANSPARENT"; _text.clickable = true; _text.onClickListener = &onTextClick; + //_text.backgroundColor = 0xC0FFFF; + _text.state = State.Parent; _button = new ImageButton(null, "scrollbar_btn_right"); + _button.styleId = "BUTTON_TRANSPARENT"; _button.focusable = false; _button.onClickListener = &onButtonClick; + //_button.backgroundColor = 0xC0FFC0; + _button.state = State.Parent; + trackHover(true); addChild(_text); addChild(_button); margins(Rect(2,0,2,0)); @@ -344,15 +358,48 @@ class FilePathPanelItem : HorizontalLayout { return false; } private bool onButtonClick(Widget src) { - // TODO: show popup menu with subdirs + // show popup menu with subdirs + string[] filters; + DirEntry[] entries; + if (!listDirectory(_path, true, false, false, filters, entries)) + return false; + if (entries.length == 0) + return false; + MenuItem dirs = new MenuItem(); + int itemId = 25000; + foreach(ref DirEntry e; entries) { + string fullPath = e.name; + string d = baseName(fullPath); + Action a = new Action(itemId++, toUTF32(d)); + MenuItem item = new MenuItem(a); + item.onMenuItemClick = delegate(MenuItem item) { + if (onPathSelectionListener.assigned) + return onPathSelectionListener(fullPath); + return false; + }; + dirs.add(item); + } + PopupMenu menuWidget = new PopupMenu(dirs); + PopupWidget popup = window.showPopup(menuWidget, this, PopupAlign.Below); + popup.flags = PopupFlags.CloseOnClickOutside; return true; } } +/// Panel with buttons - path segments - for fast navigation to subdirs. class FilePathPanelButtons : WidgetGroup { protected string _path; + Listener!OnPathSelectionHandler onPathSelectionListener; + bool onPathSelected(string path) { + if (onPathSelectionListener.assigned) { + return onPathSelectionListener(path); + } + return false; + } this(string ID = null) { super(ID); + layoutWidth = FILL_PARENT; + clickable = true; } void init(string path) { _path = path; @@ -360,8 +407,9 @@ class FilePathPanelButtons : WidgetGroup { string itemPath = path; for (;;) { FilePathPanelItem item = new FilePathPanelItem(itemPath); + item.onPathSelectionListener = &onPathSelected; addChild(item); - if (isRoot(path)) { + if (isRoot(itemPath)) { break; } itemPath = parentDir(itemPath); @@ -378,7 +426,10 @@ class FilePathPanelButtons : WidgetGroup { pwidth -= m.left + m.right + p.left + p.right; if (parentHeight != SIZE_UNSPECIFIED) pheight -= m.top + m.bottom + p.top + p.bottom; - int reservedForEmptySpace = parentWidth / 16; + int reservedForEmptySpace = parentWidth / 20; + if (reservedForEmptySpace > 40) + reservedForEmptySpace = 40; + Point sz; sz.x += reservedForEmptySpace; // measure children @@ -410,7 +461,9 @@ class FilePathPanelButtons : WidgetGroup { applyMargins(rc); applyPadding(rc); - int reservedForEmptySpace = rc.width / 16; + int reservedForEmptySpace = rc.width / 20; + if (reservedForEmptySpace > 40) + reservedForEmptySpace = 40; int maxw = rc.width - reservedForEmptySpace; int totalw = 0; int visibleItems = 0; @@ -437,33 +490,90 @@ class FilePathPanelButtons : WidgetGroup { int w = item.measuredWidth; if (i == visibleItems - 1 && w > maxw) w = maxw; - rc.right = rc.left + w; - item.layout(rc); - rc.left += w; + itemRect.right = itemRect.left + w; + item.layout(itemRect); + itemRect.left += w; } } + /// Draw widget at its position to buffer + override void onDraw(DrawBuf buf) { + if (visibility != Visibility.Visible) + return; + super.onDraw(buf); + Rect rc = _pos; + applyMargins(rc); + applyPadding(rc); + auto saver = ClipRectSaver(buf, rc); + for (int i = 0; i < _children.count; i++) { + Widget item = _children.get(i); + if (item.visibility != Visibility.Visible) + continue; + item.onDraw(buf); + } + } + +} + +interface PathSelectedHandler { + bool onPathSelected(string path); } class FilePathPanel : FrameLayout { + Listener!OnPathSelectionHandler onPathSelectionListener; static const ID_SEGMENTS = "SEGMENTS"; static const ID_EDITOR = "ED_PATH"; protected FilePathPanelButtons _segments; protected EditLine _edPath; protected string _path; + Signal!PathSelectedHandler pathListener; this(string ID = null) { super(ID); _segments = new FilePathPanelButtons(ID_SEGMENTS); _edPath = new EditLine(ID_EDITOR); + _edPath.layoutWidth = FILL_PARENT; + _edPath.editorActionListener = &onEditorAction; + _edPath.onFocusChangeListener = &onEditorFocusChanged; + _segments.onClickListener = &onSegmentsClickOutside; + _segments.onPathSelectionListener = &onPathSelected; addChild(_segments); addChild(_edPath); - showChild(ID_SEGMENTS); } + protected bool onEditorFocusChanged(Widget source, bool focused) { + if (!focused) { + _edPath.text = toUTF32(_path); + showChild(ID_SEGMENTS); + } + return true; + } + protected bool onPathSelected(string path) { + if (onPathSelectionListener.assigned) { + if (exists(path)) + return onPathSelectionListener(path); + } + return false; + } + bool onSegmentsClickOutside(Widget w) { + // switch to editor + showChild(ID_EDITOR); + _edPath.setFocus(); + return true; + } + bool onEditorAction(const Action action) { + if (action.id == EditorActions.InsertNewLine) { + string fn = buildNormalizedPath(toUTF8(_edPath.text)); + if (exists(fn) && isDir(fn)) + return onPathSelected(fn); + } + return false; + } + @property void path(string value) { _segments.init(value); _edPath.text = toUTF32(value); _path = path; + showChild(ID_SEGMENTS); } @property string path() { return _path; diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 0ebdbee6..1d655eed 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -1708,10 +1708,15 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction } +interface EditorActionHandler { + bool onEditorAction(const Action action); +} /// single line editor class EditLine : EditWidgetBase { + Signal!EditorActionHandler editorActionListener; + /// empty parameter list constructor - for usage by factory this() { this(null); @@ -1797,6 +1802,12 @@ class EditLine : EditWidgetBase { override protected bool handleAction(const Action a) { switch (a.id) { + case EditorActions.InsertNewLine: + case EditorActions.PrependNewLine: + if (editorActionListener.assigned) { + return editorActionListener(a); + } + break; case EditorActions.Up: break; case EditorActions.Down: diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d index 9014514e..ac2903db 100644 --- a/src/dlangui/widgets/menu.d +++ b/src/dlangui/widgets/menu.d @@ -88,7 +88,7 @@ class MenuItem { } /// set new MenuItemType - @property MenuItem type(MenuItemType type) { + @property MenuItem type(MenuItemType type) { _type = type; return this; }