FileDialog fixes

This commit is contained in:
Vadim Lopatin 2014-12-22 15:18:22 +03:00
parent 570b7f0c92
commit 7fc0e7eee9
10 changed files with 154 additions and 28 deletions

View File

@ -64,7 +64,7 @@
<doXGeneration>1</doXGeneration>
<xfilename>$(IntDir)\$(TargetName).json</xfilename>
<debuglevel>0</debuglevel>
<debugids />
<debugids>DebugFocus</debugids>
<versionlevel>0</versionlevel>
<versionids>USE_SDL USE_OPENGL</versionids>
<dump_source>0</dump_source>

View File

@ -293,8 +293,8 @@ extern (C) int UIAppMain(string[] args) {
UIString caption;
caption = "Open Text File"d;
FileDialog dlg = new FileDialog(caption, window, null);
dlg.onDialogResult = delegate(Dialog dlg, Action result) {
Log.d("FileDialog.onDialogResult");
dlg.onDialogResult = delegate(Dialog dlg, const Action result) {
Log.d("FileDialog.onDialogResult: ", result, " param=", result.stringParam);
};
dlg.show();
return true;

View File

@ -66,7 +66,7 @@ class Action {
/// optional object parameter
protected Object _objectParam;
/// returns optional string parameter
@property string stringParam() {
@property string stringParam() const {
return _stringParam;
}
/// sets optional string parameter
@ -107,6 +107,10 @@ class Action {
}
/// deep copy
@property Action clone() immutable { return new Action(this); }
/// deep copy
@property Action clone() const { return new Action(cast(immutable)this); }
/// deep copy
@property Action clone() { return new Action(cast(immutable)this); }
/// create action only with ID
this(int id) {
_id = id;
@ -185,6 +189,10 @@ class Action {
_iconId = id;
return this;
}
override string toString() const {
return "Action(" ~ to!string(_id) ~ ")";
}
}
/// Map of Accelerator to Action

View File

@ -34,7 +34,7 @@ enum DialogFlag : uint {
}
interface DialogResultHandler {
public void onDialogResult(Dialog dlg, Action result);
public void onDialogResult(Dialog dlg, const Action result);
}
/// base for all dialogs
@ -88,8 +88,11 @@ class Dialog : VerticalLayout {
return this;
}
protected const(Action) [] _buttonActions;
/// create panel with buttons based on list of actions
Widget createButtonsPanel(const Action[] actions, int defaultActionIndex, int splitBeforeIndex) {
Widget createButtonsPanel(const(Action) [] actions, int defaultActionIndex, int splitBeforeIndex) {
_buttonActions = actions;
LinearLayout res = new HorizontalLayout("buttons");
res.layoutWidth(FILL_PARENT);
res.layoutWeight = 0;
@ -97,18 +100,26 @@ class Dialog : VerticalLayout {
if (splitBeforeIndex == i)
res.addChild(new HSpacer());
const Action a = actions[i];
string id = "btn" ~ to!string(a.id);
string id = "btn" ~ to!string(a.id);
ImageTextButton btn = new ImageTextButton(id, a.iconId, a.label);
if (defaultActionIndex == i)
btn.setState(State.Default);
btn.onClickListener = delegate(Widget source) {
return handleAction(a);
};
btn.action = a.clone();
res.addChild(btn);
}
return res;
}
/// Custom handling of actions
override bool handleAction(const Action action) {
foreach(const Action a; _buttonActions)
if (a.id == action.id) {
close(action);
return true;
}
return false;
}
/// override to implement creation of dialog controls
void init() {
}
@ -121,7 +132,7 @@ class Dialog : VerticalLayout {
If action is null, no result dispatching will occur.
*/
void close(Action action) {
void close(const Action action) {
if (action) {
if (onDialogResult.assigned)
onDialogResult(this, action);
@ -144,5 +155,11 @@ class Dialog : VerticalLayout {
_window.windowIcon = drawableCache.getImage(_icon);
_window.mainWidget = this;
_window.show();
onShow();
}
/// called after window with dialog is shown
void onShow() {
// override to do something useful
}
}

View File

@ -36,6 +36,7 @@ import dlangui.widgets.editors;
import dlangui.platforms.common.platform;
import dlangui.dialogs.dialog;
private import std.algorithm;
private import std.file;
private import std.path;
private import std.utf;
@ -70,8 +71,17 @@ class FileDialog : Dialog, CustomGridCellAdapter {
protected DirEntry[] _entries;
protected bool _isRoot;
protected bool _isOpenDialog;
this(UIString caption, Window parent, Action action = null, uint fileDialogFlags = DialogFlag.Modal | DialogFlag.Resizable | FileDialogFlag.FileMustExist) {
super(caption, parent, fileDialogFlags);
_isOpenDialog = !(_flags & FileDialogFlag.ConfirmOverwrite);
if (action is null) {
if (_isOpenDialog)
action = ACTION_OPEN.clone();
else
action = ACTION_SAVE.clone();
}
_action = action;
}
@ -82,7 +92,11 @@ class FileDialog : Dialog, CustomGridCellAdapter {
_fileList.fillColumnWidth(1);
}
protected bool openDirectory(string dir) {
protected bool upLevel() {
return openDirectory(buildNormalizedPath(_path, ".."), _path);
}
protected bool openDirectory(string dir, string selectedItemPath) {
dir = buildNormalizedPath(dir);
Log.d("FileDialog.openDirectory(", dir, ")");
_fileList.rows = 0;
@ -93,7 +107,10 @@ class FileDialog : Dialog, CustomGridCellAdapter {
_isRoot = isRoot(dir);
_edPath.text = toUTF32(_path);
_fileList.rows = cast(int)_entries.length;
int selectionIndex = -1;
for (int i = 0; i < _entries.length; i++) {
if (_entries[i].name.equal(selectedItemPath))
selectionIndex = i;
string fname = baseName(_entries[i].name);
string sz;
string date;
@ -111,9 +128,23 @@ class FileDialog : Dialog, CustomGridCellAdapter {
}
_fileList.autoFitColumnWidths();
_fileList.fillColumnWidth(1);
if (selectionIndex >= 0)
_fileList.selectCell(1, selectionIndex + 1, true);
else if (_entries.length > 0)
_fileList.selectCell(1, 1, true);
return true;
}
override bool onKeyEvent(KeyEvent event) {
if (event.action == KeyAction.KeyDown) {
if (event.keyCode == KeyCode.BACK && event.flags == 0) {
upLevel();
return true;
}
}
return false;
}
/// return true for custom drawn cell
override bool isCustomCell(int col, int row) {
if (col == 0 && row >= 0)
@ -150,8 +181,9 @@ class FileDialog : Dialog, CustomGridCellAdapter {
}
}
protected Widget createRootsList() {
protected ListWidget createRootsList() {
ListWidget res = new ListWidget("ROOTS_LIST");
res.styleId = "EDIT_BOX";
WidgetListAdapter adapter = new WidgetListAdapter();
foreach(ref RootEntry root; _roots) {
ImageTextButton btn = new ImageTextButton(null, root.icon, root.label);
@ -161,28 +193,61 @@ class FileDialog : Dialog, CustomGridCellAdapter {
adapter.widgets.add(btn);
}
res.ownAdapter = adapter;
res.layoutWidth = WRAP_CONTENT;
res.layoutHeight = FILL_PARENT;
res.layoutWidth(WRAP_CONTENT).layoutHeight(FILL_PARENT).layoutWeight(0);
res.onItemClickListener = delegate(Widget source, int itemIndex) {
openDirectory(_roots[itemIndex].path);
openDirectory(_roots[itemIndex].path, null);
return true;
};
res.focusable = true;
debug Log.d("root lisk styleId=", res.styleId);
return res;
}
/// file list item activated (double clicked or Enter key pressed)
protected void onItemActivated(int index) {
DirEntry e = _entries[index];
if (e.isDir) {
openDirectory(e.name);
openDirectory(e.name, _path);
} else if (e.isFile) {
string fname = e.name;
Action result = ACTION_OPEN.clone();
Action result = _action;
result.stringParam = fname;
close(result);
}
}
/// file list item selected
protected void onItemSelected(int index) {
DirEntry e = _entries[index];
if (e.isDir) {
_edFilename.text = ""d;
_filename = "";
} else if (e.isFile) {
string fname = e.name;
_edFilename.text = toUTF32(baseName(fname));
_filename = fname;
}
}
/// Custom handling of actions
override bool handleAction(const Action action) {
if (action.id == StandardAction.Cancel) {
super.handleAction(action);
return true;
}
if (action.id == StandardAction.Open || action.id == StandardAction.Save) {
if (_filename.length > 0) {
Action result = _action;
result.stringParam = _filename;
close(result);
return true;
}
}
return super.handleAction(action);
}
/// override to implement creation of dialog controls
override void init() {
_roots = getRootPaths;
@ -211,7 +276,7 @@ class FileDialog : Dialog, CustomGridCellAdapter {
_edPath.layoutWidth(FILL_PARENT);
_edPath.layoutWeight = 0;
_edFilename = new EditLine("path");
_edFilename = new EditLine("filename");
_edFilename.layoutWidth(FILL_PARENT);
_edFilename.layoutWeight = 0;
@ -230,16 +295,22 @@ class FileDialog : Dialog, CustomGridCellAdapter {
addChild(content);
addChild(createButtonsPanel([ACTION_OPEN, ACTION_CANCEL], 0, 0));
addChild(createButtonsPanel([cast(immutable)_action, ACTION_CANCEL], 0, 0));
_fileList.customCellAdapter = this;
_fileList.onCellActivated = delegate(GridWidgetBase source, int col, int row) {
onItemActivated(row);
};
_fileList.onCellSelected = delegate(GridWidgetBase source, int col, int row) {
onItemSelected(row);
};
openDirectory(currentDir);
openDirectory(currentDir, null);
_fileList.layoutHeight = FILL_PARENT;
_fileList.setFocus();
}
override void onShow() {
_fileList.setFocus();
}
}

View File

@ -311,8 +311,11 @@ class Window {
if (focus.onKeyEvent(event))
return true; // processed by focused widget
}
if (_mainWidget)
return dispatchKeyEvent(_mainWidget, event) || res;
if (_mainWidget) {
if (dispatchKeyEvent(_mainWidget, event))
return res;
return _mainWidget.onKeyEvent(event) || res;
}
return res;
}
@ -431,7 +434,7 @@ class Window {
}
/// dispatch action to main widget
bool dispatchAction(Action action) {
bool dispatchAction(const Action action) {
Widget focus = focusedWidget;
// first, offer action to focused widget
if (focus && focus.handleAction(action))

View File

@ -191,6 +191,7 @@ class ImageWidget : Widget {
}
}
/// button with image only
class ImageButton : ImageWidget {
this(string ID = null, string drawableId = null) {
@ -201,6 +202,8 @@ class ImageButton : ImageWidget {
focusable = true;
trackHover = true;
}
}
/// button with image and text

View File

@ -200,6 +200,8 @@ enum GridActions : int {
DocumentEnd,
/// move cursor to the end of document with selection
SelectDocumentEnd,
/// Enter key pressed on cell
ActivateCell,
}
/// Adapter for custom drawing of some cells in grid widgets
@ -610,7 +612,7 @@ class GridWidgetBase : ScrollWidgetBase {
if (makeVisible)
makeCellVisible(_col, _row);
if (onCellSelected.assigned)
onCellSelected(this, _col, _row);
onCellSelected(this, _col - _headerCols, _row - _headerRows);
return true;
}
@ -799,6 +801,12 @@ class GridWidgetBase : ScrollWidgetBase {
}
switch (actionId) {
case GridActions.ActivateCell:
if (onCellActivated.assigned) {
onCellActivated(this, col, row);
return true;
}
return false;
case GridActions.ScrollLeft:
scrollBy(-1, 0);
return true;
@ -1103,6 +1111,7 @@ class GridWidgetBase : ScrollWidgetBase {
new Action(GridActions.PageEnd, KeyCode.PAGEDOWN, KeyFlag.Control),
new Action(GridActions.DocumentBegin, KeyCode.HOME, KeyFlag.Control),
new Action(GridActions.DocumentEnd, KeyCode.END, KeyFlag.Control),
new Action(GridActions.ActivateCell, KeyCode.RETURN, 0),
]);
focusable = true;
}

View File

@ -780,7 +780,7 @@ class ListWidget : WidgetGroup, OnScrollHandler {
// TODO
}
}
return false;
return super.onKeyEvent(event);
//if (_selectedItemIndex != -1 && event.action == KeyAction.KeyUp && (event.keyCode == KeyCode.SPACE || event.keyCode == KeyCode.RETURN)) {
// itemClicked(_selectedItemIndex);
// return true;

View File

@ -596,6 +596,13 @@ class Widget {
return false;
}
protected Action _action;
/// action to emit on click
@property Action action() { return _action; }
/// action to emit on click
@property void action(Action action) { _action = action; }
protected bool _focusGroup;
/*****************************************
* When focus group is set for some parent widget, focus from one of containing widgets can be moved using keyboard only to one of other widgets containing in it and cannot bypass bounds of focusGroup.
@ -668,6 +675,9 @@ class Widget {
}
return obj1.rect.left < obj2.rect.left;
}
override string toString() {
return widget.id;
}
}
private void findFocusableChildren(ref TabOrderInfo[] results, Rect clipRect) {
@ -727,6 +737,7 @@ class Widget {
break;
}
}
debug(DebugFocus) Log.d("findNextFocusWidget myIndex=", myIndex, " of focusables: ", focusables);
if (myIndex == -1)
return null; // not found myself
if (focusables.length == 1)
@ -876,7 +887,11 @@ class Widget {
// called to process click and notify listeners
protected bool handleClick() {
bool res = onClickListener(this);
bool res = false;
if (onClickListener.assigned)
res = onClickListener(this);
else if (_action)
res = handleAction(_action);
return res;
}