list fixes

This commit is contained in:
Vadim Lopatin 2014-04-16 14:39:14 +04:00
parent c18f9ee890
commit 716027aab4
5 changed files with 149 additions and 44 deletions

View File

@ -3,7 +3,7 @@ module dlangui.core.signals;
import std.traits;
import dlangui.core.collections;
/// parameter is interface with single method
/// Single listener; parameter is interface with single method
struct Listener(T1) if (is(T1 == interface) && __traits(allMembers, T1).length == 1) {
alias return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0]));
alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0]));
@ -37,7 +37,7 @@ struct Listener(T1) if (is(T1 == interface) && __traits(allMembers, T1).length =
alias get this;
}
/// implicitly specified return and parameter types
/// Single listener; implicitly specified return and parameter types
struct Listener(RETURN_T, T1...)
{
alias slot_t = RETURN_T delegate(T1);
@ -65,7 +65,7 @@ struct Listener(RETURN_T, T1...)
alias get this;
}
/// implicitly specified return and parameter types
/// Multiple listeners; implicitly specified return and parameter types
struct Signal(T1) if (is(T1 == interface) && __traits(allMembers, T1).length == 1) {
alias return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0]));
alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0]));
@ -75,6 +75,7 @@ struct Signal(T1) if (is(T1 == interface) && __traits(allMembers, T1).length ==
final bool assigned() {
return _listeners.length > 0;
}
/// replace all previously assigned listeners with new one (if null passed, remove all listeners)
final void opAssign(slot_t listener) {
_listeners.clear();
_listeners ~= listener;
@ -116,7 +117,7 @@ struct Signal(T1) if (is(T1 == interface) && __traits(allMembers, T1).length ==
}
}
/// implicitly specified return and parameter types
/// Multiple listeners; implicitly specified return and parameter types
struct Signal(RETURN_T, T1...)
{
alias slot_t = RETURN_T delegate(T1);

View File

@ -90,6 +90,10 @@ struct Rect {
bool isPointInside(int x, int y) {
return x >= left && x < right && y >= top && y < bottom;
}
/// this rectangle is completely inside rc
bool isInsideOf(Rect rc) {
return left >= rc.left && right <= rc.right && top >= rc.top && bottom <= rc.bottom;
}
}
/// character glyph

View File

@ -101,11 +101,11 @@ class ListWidget : WidgetGroup, OnScrollHandler {
Rect itemRect(int index) {
Rect res = itemRectNoScroll(index);
if (_orientation == Orientation.Horizontal) {
res.left += _scrollPosition;
res.right += _scrollPosition;
res.left -= _scrollPosition;
res.right -= _scrollPosition;
} else {
res.top += _scrollPosition;
res.bottom += _scrollPosition;
res.top -= _scrollPosition;
res.bottom -= _scrollPosition;
}
return res;
}
@ -192,16 +192,91 @@ class ListWidget : WidgetGroup, OnScrollHandler {
protected void itemClicked(int index) {
}
protected void selectItem(int index) {
if (_selectedItemIndex == index)
return;
protected void updateSelectedItemFocus() {
if (_selectedItemIndex != -1) {
_adapter.resetItemState(_selectedItemIndex, State.Selected);
if ((_adapter.itemState(_selectedItemIndex) & State.Focused) != (state & State.Focused)) {
if (state & State.Focused)
_adapter.setItemState(_selectedItemIndex, State.Focused);
else
_adapter.resetItemState(_selectedItemIndex, State.Focused);
invalidate();
}
}
}
/// override to handle focus changes
override protected void onFocusChange(bool focused) {
updateSelectedItemFocus();
}
/// ensure selected item is visible (scroll if necessary)
void makeSelectionVisible() {
if (_selectedItemIndex < 0)
return; // no selection
makeItemVisible(_selectedItemIndex);
}
/// ensure item is visible
void makeItemVisible(int itemIndex) {
if (itemIndex < 0 || itemIndex >= itemCount)
return; // no selection
Rect viewrc = Rect(0, 0, _clientRc.width, _clientRc.height);
Rect scrolledrc = itemRect(itemIndex);
if (scrolledrc.isInsideOf(viewrc)) // completely visible
return;
int delta = 0;
if (_orientation == Orientation.Vertical) {
if (scrolledrc.top < viewrc.top)
delta = scrolledrc.top - viewrc.top;
else if (scrolledrc.bottom > viewrc.bottom)
delta = scrolledrc.bottom - viewrc.bottom;
} else {
if (scrolledrc.left < viewrc.left)
delta = scrolledrc.left - viewrc.left;
else if (scrolledrc.right > viewrc.right)
delta = scrolledrc.right - viewrc.right;
}
int newPosition = _scrollPosition + delta;
_scrollbar.position = newPosition;
_scrollPosition = newPosition;
invalidate();
}
/// move selection
void moveSelection(int direction, bool wrapAround = true) {
if (itemCount <= 0)
return;
if (_selectedItemIndex < 0) {
// no previous selection
if (direction > 0)
selectItem(wrapAround ? 0 : itemCount - 1);
else
selectItem(wrapAround ? itemCount - 1 : 0);
return;
}
int newIndex = _selectedItemIndex + direction;
if (newIndex < 0)
newIndex = wrapAround ? itemCount - 1 : 0;
else if (newIndex >= itemCount)
newIndex = wrapAround ? 0 : itemCount - 1;
if (newIndex != _selectedItemIndex)
selectItem(newIndex);
}
protected void selectItem(int index) {
if (_selectedItemIndex == index) {
updateSelectedItemFocus();
makeSelectionVisible();
return;
}
if (_selectedItemIndex != -1) {
_adapter.resetItemState(_selectedItemIndex, State.Selected | State.Focused);
invalidate();
}
_selectedItemIndex = index;
if (_selectedItemIndex != -1) {
_adapter.setItemState(_selectedItemIndex, State.Selected);
makeSelectionVisible();
_adapter.setItemState(_selectedItemIndex, State.Selected | (state & State.Focused));
invalidate();
}
}
@ -488,28 +563,34 @@ class ListWidget : WidgetGroup, OnScrollHandler {
navigationDelta = -1;
}
}
if (_selectedItemIndex != -1 && event.action == KeyAction.KeyUp && (event.keyCode == KeyCode.SPACE || event.keyCode == KeyCode.RETURN)) {
itemClicked(_selectedItemIndex);
return true;
}
if (navigationDelta != 0) {
int p = _selectedItemIndex;
if (p < 0) {
if (navigationDelta < 0)
p = itemCount - 1;
else
p = 0;
} else {
p += navigationDelta;
if (p < 0)
p = itemCount - 1;
else if (p >= itemCount)
p = 0;
}
selectItem(p);
moveSelection(navigationDelta);
return true;
}
return false;
//if (_selectedItemIndex != -1 && event.action == KeyAction.KeyUp && (event.keyCode == KeyCode.SPACE || event.keyCode == KeyCode.RETURN)) {
// itemClicked(_selectedItemIndex);
// return true;
//}
//if (navigationDelta != 0) {
// int p = _selectedItemIndex;
// if (p < 0) {
// if (navigationDelta < 0)
// p = itemCount - 1;
// else
// p = 0;
// } else {
// p += navigationDelta;
// if (p < 0)
// p = itemCount - 1;
// else if (p >= itemCount)
// p = 0;
// }
// setHoverItem(-1);
// selectItem(p);
// return true;
//}
//return false;
}
/// process mouse event; return true if event is processed by widget.

View File

@ -106,6 +106,7 @@ class MenuWidgetBase : ListWidget {
_openedPopup = null;
_openedMenu = null;
selectItem(-1);
setHoverItem(-1);
window.setFocus(this);
}
@ -154,6 +155,7 @@ class MenuWidgetBase : ListWidget {
// top level handling
Log.d("onMenuItem ", item.id);
selectItem(-1);
setHoverItem(-1);
selectOnHover = false;
bool delegate(MenuItem item) listener = _onMenuItemClickListener;
PopupWidget popup = cast(PopupWidget)parent;
@ -207,10 +209,16 @@ class MenuWidgetBase : ListWidget {
} else {
if (event.action == KeyAction.KeyDown) {
if (event.keyCode == KeyCode.LEFT) {
if (_parentMenu !is null && _parentMenu.orientation == Orientation.Vertical) {
if (thisPopup !is null) {
// back to parent menu on Left key
thisPopup.close();
if (_parentMenu !is null) {
if (_parentMenu.orientation == Orientation.Vertical) {
if (thisPopup !is null) {
// back to parent menu on Left key
thisPopup.close();
return true;
}
} else {
// parent is main menu
_parentMenu.moveSelection(-1);
return true;
}
}
@ -220,6 +228,9 @@ class MenuWidgetBase : ListWidget {
if (thisItem !is null && thisItem.item.isSubmenu) {
openSubmenu(thisItem, true);
return true;
} else if (_parentMenu !is null && _parentMenu.orientation == Orientation.Horizontal) {
_parentMenu.moveSelection(1);
return true;
}
return true;
}

View File

@ -9,7 +9,8 @@ public import dlangui.graphics.resources;
public import dlangui.graphics.fonts;
public import dlangui.core.i18n;
public import std.signals;
//public import std.signals;
public import dlangui.core.signals;
import dlangui.platforms.common.platform;
@ -135,12 +136,21 @@ class Widget {
return _parent.state;
return _state;
}
/// override to handle focus changes
protected void onFocusChange(bool focused) {
}
/// set new widget state (set of flags from State enum)
@property Widget state(uint newState) {
if (newState != _state) {
uint oldState = _state;
_state = newState;
// need to redraw
invalidate();
// notify focus changes
if ((oldState & State.Focused) && !(newState & State.Focused))
onFocusChange(false);
else if (!(oldState & State.Focused) && (newState & State.Focused))
onFocusChange(true);
}
return this;
}
@ -327,7 +337,7 @@ class Widget {
/// process key event, return true if event is processed.
bool onKeyEvent(KeyEvent event) {
if (_onClickListener !is null) {
if (onClickListener.assigned) {
// support onClick event initiated by Space or Return keys
if (event.action == KeyAction.KeyDown) {
if (event.keyCode == KeyCode.SPACE || event.keyCode == KeyCode.RETURN) {
@ -338,7 +348,7 @@ class Widget {
if (event.action == KeyAction.KeyUp) {
if (event.keyCode == KeyCode.SPACE || event.keyCode == KeyCode.RETURN) {
resetState(State.Pressed);
_onClickListener(this);
onClickListener(this);
return true;
}
}
@ -350,7 +360,7 @@ class Widget {
bool onMouseEvent(MouseEvent event) {
//Log.d("onMouseEvent ", id, " ", event.action, " (", event.x, ",", event.y, ")");
// support onClick
if (_onClickListener !is null) {
if (onClickListener.assigned) {
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
setState(State.Pressed);
if (focusable)
@ -359,7 +369,7 @@ class Widget {
}
if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) {
resetState(State.Pressed);
_onClickListener(this);
onClickListener(this);
return true;
}
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
@ -399,11 +409,9 @@ class Widget {
return false;
}
protected onClick_t _onClickListener;
/// on click event listener (bool delegate(Widget))
@property onClick_t onClickListener() { return _onClickListener; }
/// set on click event listener (bool delegate(Widget))
@property Widget onClickListener(onClick_t listener) { _onClickListener = listener; return this; }
Signal!OnClickHandler onClickListener;
// =======================================================
// Layout and measurement methods