mirror of https://github.com/buggins/dlangui.git
584 lines
20 KiB
D
584 lines
20 KiB
D
module dlangui.widgets.controls;
|
|
|
|
import dlangui.widgets.widget;
|
|
|
|
|
|
|
|
/// static text widget
|
|
class TextWidget : Widget {
|
|
this(string ID = null) {
|
|
super(ID);
|
|
styleId = "TEXT";
|
|
}
|
|
protected UIString _text;
|
|
/// get widget text
|
|
override @property dstring text() { return _text; }
|
|
/// set text to show
|
|
override @property Widget text(dstring s) {
|
|
_text = s;
|
|
requestLayout();
|
|
return this;
|
|
}
|
|
/// set text resource ID to show
|
|
@property Widget textResource(string s) {
|
|
_text = s;
|
|
requestLayout();
|
|
return this;
|
|
}
|
|
|
|
override void measure(int parentWidth, int parentHeight) {
|
|
FontRef font = font();
|
|
Point sz = font.textSize(text);
|
|
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
|
}
|
|
|
|
bool onClick() {
|
|
// override it
|
|
Log.d("Button.onClick ", id);
|
|
return false;
|
|
}
|
|
|
|
override void onDraw(DrawBuf buf) {
|
|
if (visibility != Visibility.Visible)
|
|
return;
|
|
super.onDraw(buf);
|
|
Rect rc = _pos;
|
|
applyMargins(rc);
|
|
ClipRectSaver(buf, rc);
|
|
applyPadding(rc);
|
|
FontRef font = font();
|
|
Point sz = font.textSize(text);
|
|
applyAlign(rc, sz);
|
|
font.drawText(buf, rc.left, rc.top, text, textColor);
|
|
}
|
|
}
|
|
|
|
/// image widget
|
|
class ImageWidget : Widget {
|
|
|
|
protected string _drawableId;
|
|
protected DrawableRef _drawable;
|
|
|
|
this(string ID = null, string drawableId = null) {
|
|
super(ID);
|
|
_drawableId = drawableId;
|
|
}
|
|
|
|
/// get drawable image id
|
|
@property string drawableId() { return _drawableId; }
|
|
/// set drawable image id
|
|
@property ImageWidget drawableId(string id) {
|
|
_drawableId = id;
|
|
_drawable.clear();
|
|
requestLayout();
|
|
return this;
|
|
}
|
|
/// get drawable
|
|
@property ref DrawableRef drawable() {
|
|
if (!_drawable.isNull)
|
|
return _drawable;
|
|
if (_drawableId !is null)
|
|
_drawable = drawableCache.get(_drawableId);
|
|
return _drawable;
|
|
}
|
|
/// set custom drawable (not one from resources)
|
|
@property ImageWidget drawable(DrawableRef img) {
|
|
_drawable = img;
|
|
_drawableId = null;
|
|
return this;
|
|
}
|
|
|
|
override void measure(int parentWidth, int parentHeight) {
|
|
DrawableRef img = drawable;
|
|
int w = 0;
|
|
int h = 0;
|
|
if (!img.isNull) {
|
|
w = img.width;
|
|
h = img.height;
|
|
}
|
|
measuredContent(parentWidth, parentHeight, w, h);
|
|
}
|
|
override void onDraw(DrawBuf buf) {
|
|
if (visibility != Visibility.Visible)
|
|
return;
|
|
super.onDraw(buf);
|
|
Rect rc = _pos;
|
|
applyMargins(rc);
|
|
ClipRectSaver(buf, rc);
|
|
applyPadding(rc);
|
|
DrawableRef img = drawable;
|
|
if (!img.isNull) {
|
|
Point sz;
|
|
sz.x = img.width;
|
|
sz.y = img.height;
|
|
applyAlign(rc, sz);
|
|
img.drawTo(buf, rc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// button with image only
|
|
class ImageButton : ImageWidget {
|
|
this(string ID = null, string drawableId = null) {
|
|
super(ID);
|
|
styleId = "BUTTON";
|
|
_drawableId = drawableId;
|
|
trackHover = true;
|
|
}
|
|
}
|
|
|
|
class Button : Widget {
|
|
protected UIString _text;
|
|
override @property dstring text() { return _text; }
|
|
override @property Widget text(dstring s) { _text = s; requestLayout(); return this; }
|
|
@property Widget textResource(string s) { _text = s; requestLayout(); return this; }
|
|
this(string ID = null) {
|
|
super(ID);
|
|
styleId = "BUTTON";
|
|
trackHover = true;
|
|
}
|
|
|
|
override void measure(int parentWidth, int parentHeight) {
|
|
FontRef font = font();
|
|
Point sz = font.textSize(text);
|
|
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
|
}
|
|
|
|
override void onDraw(DrawBuf buf) {
|
|
super.onDraw(buf);
|
|
Rect rc = _pos;
|
|
applyMargins(rc);
|
|
buf.fillRect(_pos, backgroundColor);
|
|
applyPadding(rc);
|
|
ClipRectSaver(buf, rc);
|
|
FontRef font = font();
|
|
Point sz = font.textSize(text);
|
|
applyAlign(rc, sz);
|
|
font.drawText(buf, rc.left, rc.top, text, textColor);
|
|
}
|
|
|
|
}
|
|
|
|
/// scroll event handler interface
|
|
interface OnScrollHandler {
|
|
/// handle scroll event
|
|
bool onScrollEvent(AbstractSlider source, ScrollEvent event);
|
|
}
|
|
|
|
/// base class for widgets like scrollbars and sliders
|
|
class AbstractSlider : WidgetGroup {
|
|
protected int _minValue = 0;
|
|
protected int _maxValue = 100;
|
|
protected int _pageSize = 30;
|
|
protected int _position = 20;
|
|
|
|
this(string ID) {
|
|
super(ID);
|
|
}
|
|
|
|
protected bool delegate(AbstractSlider source, ScrollEvent event) _onScrollEventListener;
|
|
/// scroll event listener
|
|
@property bool delegate(AbstractSlider source, ScrollEvent event) onScrollEventListener() const { return _onScrollEventListener; }
|
|
/// sets new scroll event listener
|
|
@property AbstractSlider onScrollEventListener(bool delegate(AbstractSlider source, ScrollEvent event) listener) { _onScrollEventListener = listener; return this; }
|
|
|
|
/// returns slider position
|
|
@property int position() const { return _position; }
|
|
/// sets new slider position
|
|
@property AbstractSlider position(int newPosition) {
|
|
if (_position != newPosition) {
|
|
_position = newPosition;
|
|
onPositionChanged();
|
|
}
|
|
return this;
|
|
}
|
|
protected void onPositionChanged() {
|
|
requestLayout();
|
|
}
|
|
/// returns slider range min value
|
|
@property int minValue() const { return _minValue; }
|
|
/// returns slider range max value
|
|
@property int maxValue() const { return _maxValue; }
|
|
/// page size (visible area size)
|
|
@property int pageSize() const { return _pageSize; }
|
|
/// set page size (visible area size)
|
|
@property AbstractSlider pageSize(int size) {
|
|
if (_pageSize != size) {
|
|
_pageSize = size;
|
|
requestLayout();
|
|
}
|
|
return this;
|
|
}
|
|
/// set new range (min and max values for slider)
|
|
AbstractSlider setRange(int min, int max) {
|
|
if (_minValue != min || _maxValue != max) {
|
|
_minValue = min;
|
|
_maxValue = max;
|
|
requestLayout();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
protected bool sendScrollEvent(ScrollAction action, int position) {
|
|
if (_onScrollEventListener is null)
|
|
return false;
|
|
ScrollEvent event = new ScrollEvent(action, _minValue, _maxValue, _pageSize, position);
|
|
bool res = _onScrollEventListener(this, event);
|
|
if (event.positionChanged) {
|
|
_position = event.position;
|
|
if (_position > _maxValue)
|
|
_position = _maxValue;
|
|
if (_position < _minValue)
|
|
_position = _minValue;
|
|
onPositionChanged();
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// scroll bar - either vertical or horizontal
|
|
class ScrollBar : AbstractSlider, OnClickHandler {
|
|
protected ImageButton _btnBack;
|
|
protected ImageButton _btnForward;
|
|
protected SliderButton _indicator;
|
|
protected PageScrollButton _pageUp;
|
|
protected PageScrollButton _pageDown;
|
|
protected Rect _scrollArea;
|
|
protected int _btnSize;
|
|
protected int _minIndicatorSize;
|
|
|
|
|
|
|
|
class PageScrollButton : Widget {
|
|
this(string ID) {
|
|
super(ID);
|
|
styleId = "PAGE_SCROLL";
|
|
trackHover = true;
|
|
}
|
|
}
|
|
|
|
class SliderButton : ImageButton {
|
|
Point _dragStart;
|
|
int _dragStartPosition;
|
|
bool _dragging;
|
|
Rect _dragStartRect;
|
|
|
|
this(string resourceId) {
|
|
super("SLIDER", resourceId);
|
|
trackHover = true;
|
|
}
|
|
|
|
/// process mouse event; return true if event is processed by widget.
|
|
override bool onMouseEvent(MouseEvent event) {
|
|
// support onClick
|
|
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
|
|
setState(State.Pressed);
|
|
_dragging = true;
|
|
_dragStart.x = event.x;
|
|
_dragStart.y = event.y;
|
|
_dragStartPosition = _position;
|
|
_dragStartRect = _pos;
|
|
sendScrollEvent(ScrollAction.SliderPressed, _position);
|
|
return true;
|
|
}
|
|
if (event.action == MouseAction.FocusOut && _dragging) {
|
|
return true;
|
|
}
|
|
if (event.action == MouseAction.Move && _dragging) {
|
|
int delta = _orientation == Orientation.Vertical ? event.y - _dragStart.y : event.x - _dragStart.x;
|
|
Rect rc = _dragStartRect;
|
|
int offset;
|
|
int space;
|
|
if (_orientation == Orientation.Vertical) {
|
|
rc.top += delta;
|
|
rc.bottom += delta;
|
|
if (rc.top < _scrollArea.top) {
|
|
rc.top = _scrollArea.top;
|
|
rc.bottom = _scrollArea.top + _dragStartRect.height;
|
|
} else if (rc.bottom > _scrollArea.bottom) {
|
|
rc.top = _scrollArea.bottom - _dragStartRect.height;
|
|
rc.bottom = _scrollArea.bottom;
|
|
}
|
|
offset = rc.top - _scrollArea.top;
|
|
space = _scrollArea.height - rc.height;
|
|
} else {
|
|
rc.left += delta;
|
|
rc.right += delta;
|
|
if (rc.left < _scrollArea.left) {
|
|
rc.left = _scrollArea.left;
|
|
rc.right = _scrollArea.left + _dragStartRect.width;
|
|
} else if (rc.right > _scrollArea.right) {
|
|
rc.left = _scrollArea.right - _dragStartRect.width;
|
|
rc.right = _scrollArea.right;
|
|
}
|
|
offset = rc.left - _scrollArea.left;
|
|
space = _scrollArea.width - rc.width;
|
|
}
|
|
layoutButtons(rc);
|
|
//_pos = rc;
|
|
int position = space > 0 ? _minValue + offset * (_maxValue - _minValue - _pageSize) / space : 0;
|
|
invalidate();
|
|
onIndicatorDragging(_dragStartPosition, position);
|
|
return true;
|
|
}
|
|
if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) {
|
|
resetState(State.Pressed);
|
|
if (_dragging) {
|
|
sendScrollEvent(ScrollAction.SliderReleased, _position);
|
|
_dragging = false;
|
|
}
|
|
return true;
|
|
}
|
|
if (event.action == MouseAction.Move && trackHover) {
|
|
if (!(state & State.Hover)) {
|
|
Log.d("Hover ", id);
|
|
setState(State.Hover);
|
|
}
|
|
return true;
|
|
}
|
|
if ((event.action == MouseAction.Leave || event.action == MouseAction.Cancel) && trackHover) {
|
|
Log.d("Leave ", id);
|
|
resetState(State.Hover);
|
|
return true;
|
|
}
|
|
if (event.action == MouseAction.Cancel) {
|
|
Log.d("SliderButton.onMouseEvent event.action == MouseAction.Cancel");
|
|
resetState(State.Pressed);
|
|
_dragging = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
protected bool onIndicatorDragging(int initialPosition, int currentPosition) {
|
|
_position = currentPosition;
|
|
return sendScrollEvent(ScrollAction.SliderMoved, currentPosition);
|
|
}
|
|
|
|
private bool calcButtonSizes(int availableSize, ref int spaceBackSize, ref int spaceForwardSize, ref int indicatorSize) {
|
|
int dv = _maxValue - _minValue;
|
|
if (_pageSize >= dv) {
|
|
// full size
|
|
spaceBackSize = spaceForwardSize = 0;
|
|
indicatorSize = availableSize;
|
|
return false;
|
|
}
|
|
if (dv < 0)
|
|
dv = 0;
|
|
indicatorSize = _pageSize * availableSize / dv;
|
|
if (indicatorSize < _minIndicatorSize)
|
|
indicatorSize = _minIndicatorSize;
|
|
if (indicatorSize >= availableSize) {
|
|
// full size
|
|
spaceBackSize = spaceForwardSize = 0;
|
|
indicatorSize = availableSize;
|
|
return false;
|
|
}
|
|
int spaceLeft = availableSize - indicatorSize;
|
|
int topv = _position - _minValue;
|
|
int bottomv = _position + _pageSize - _minValue;
|
|
if (topv < 0)
|
|
topv = 0;
|
|
if (bottomv > dv)
|
|
bottomv = dv;
|
|
bottomv = dv - bottomv;
|
|
spaceBackSize = spaceLeft * topv / (topv + bottomv);
|
|
spaceForwardSize = spaceLeft - spaceBackSize;
|
|
return true;
|
|
}
|
|
|
|
protected Orientation _orientation = Orientation.Vertical;
|
|
/// returns scrollbar orientation (Vertical, Horizontal)
|
|
@property Orientation orientation() { return _orientation; }
|
|
/// sets scrollbar orientation
|
|
@property ScrollBar orientation(Orientation value) {
|
|
if (_orientation != value) {
|
|
_orientation = value;
|
|
_btnBack.drawableId = style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_UP : ATTR_SCROLLBAR_BUTTON_LEFT);
|
|
_btnForward.drawableId = style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_DOWN : ATTR_SCROLLBAR_BUTTON_RIGHT);
|
|
_indicator.drawableId = style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_INDICATOR_VERTICAL : ATTR_SCROLLBAR_INDICATOR_HORIZONTAL);
|
|
requestLayout();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
this(string ID = null, Orientation orient = Orientation.Vertical) {
|
|
super(ID);
|
|
styleId = "SCROLLBAR";
|
|
_orientation = orient;
|
|
_btnBack = new ImageButton("BACK", style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_UP : ATTR_SCROLLBAR_BUTTON_LEFT));
|
|
_btnForward = new ImageButton("FORWARD", style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_BUTTON_DOWN : ATTR_SCROLLBAR_BUTTON_RIGHT));
|
|
_pageUp = new PageScrollButton("PAGE_UP");
|
|
_pageDown = new PageScrollButton("PAGE_DOWN");
|
|
_btnBack.styleId("SCROLLBAR_BUTTON");
|
|
_btnForward.styleId("SCROLLBAR_BUTTON");
|
|
_indicator = new SliderButton(style.customDrawableId(_orientation == Orientation.Vertical ? ATTR_SCROLLBAR_INDICATOR_VERTICAL : ATTR_SCROLLBAR_INDICATOR_HORIZONTAL));
|
|
addChild(_btnBack);
|
|
addChild(_btnForward);
|
|
addChild(_indicator);
|
|
addChild(_pageUp);
|
|
addChild(_pageDown);
|
|
_btnBack.onClickListener = &onClick;
|
|
_btnForward.onClickListener = &onClick;
|
|
_pageUp.onClickListener = &onClick;
|
|
_pageDown.onClickListener = &onClick;
|
|
}
|
|
|
|
override void measure(int parentWidth, int parentHeight) {
|
|
Point sz;
|
|
_btnBack.measure(parentWidth, parentHeight);
|
|
_btnForward.measure(parentWidth, parentHeight);
|
|
_indicator.measure(parentWidth, parentHeight);
|
|
_pageUp.measure(parentWidth, parentHeight);
|
|
_pageDown.measure(parentWidth, parentHeight);
|
|
_btnSize = _btnBack.measuredWidth;
|
|
_minIndicatorSize = _orientation == Orientation.Vertical ? _indicator.measuredHeight : _indicator.measuredWidth;
|
|
if (_btnSize < _btnBack.measuredHeight)
|
|
_btnSize = _btnBack.measuredHeight;
|
|
if (_btnSize < 16)
|
|
_btnSize = 16;
|
|
if (_orientation == Orientation.Vertical) {
|
|
// vertical
|
|
sz.x = _btnSize;
|
|
sz.y = _btnSize * 5; // min height
|
|
} else {
|
|
// horizontal
|
|
sz.y = _btnSize;
|
|
sz.x = _btnSize * 5; // min height
|
|
}
|
|
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
|
}
|
|
|
|
protected void layoutButtons(Rect irc) {
|
|
Rect r;
|
|
_indicator.visibility = Visibility.Visible;
|
|
if (_orientation == Orientation.Vertical) {
|
|
_indicator.layout(irc);
|
|
if (_scrollArea.top < irc.top) {
|
|
r = _scrollArea;
|
|
r.bottom = irc.top;
|
|
_pageUp.layout(r);
|
|
_pageUp.visibility = Visibility.Visible;
|
|
} else {
|
|
_pageUp.visibility = Visibility.Invisible;
|
|
}
|
|
if (_scrollArea.bottom > irc.bottom) {
|
|
r = _scrollArea;
|
|
r.top = irc.bottom;
|
|
_pageDown.layout(r);
|
|
_pageDown.visibility = Visibility.Visible;
|
|
} else {
|
|
_pageDown.visibility = Visibility.Invisible;
|
|
}
|
|
} else {
|
|
_indicator.layout(irc);
|
|
if (_scrollArea.left < irc.left) {
|
|
r = _scrollArea;
|
|
r.right = irc.left;
|
|
_pageUp.layout(r);
|
|
_pageUp.visibility = Visibility.Visible;
|
|
} else {
|
|
_pageUp.visibility = Visibility.Invisible;
|
|
}
|
|
if (_scrollArea.right > irc.right) {
|
|
r = _scrollArea;
|
|
r.left = irc.right;
|
|
_pageDown.layout(r);
|
|
_pageDown.visibility = Visibility.Visible;
|
|
} else {
|
|
_pageDown.visibility = Visibility.Invisible;
|
|
}
|
|
}
|
|
}
|
|
|
|
override void layout(Rect rc) {
|
|
applyMargins(rc);
|
|
applyPadding(rc);
|
|
Rect r;
|
|
if (_orientation == Orientation.Vertical) {
|
|
// vertical
|
|
// buttons
|
|
int backbtnpos = rc.top + _btnSize;
|
|
int fwdbtnpos = rc.bottom - _btnSize;
|
|
r = rc;
|
|
r.bottom = backbtnpos;
|
|
_btnBack.layout(r);
|
|
r = rc;
|
|
r.top = fwdbtnpos;
|
|
_btnForward.layout(r);
|
|
// indicator
|
|
r = rc;
|
|
r.top = backbtnpos;
|
|
r.bottom = fwdbtnpos;
|
|
_scrollArea = r;
|
|
int spaceBackSize, spaceForwardSize, indicatorSize;
|
|
bool indicatorVisible = calcButtonSizes(r.height, spaceBackSize, spaceForwardSize, indicatorSize);
|
|
Rect irc = r;
|
|
irc.top += spaceBackSize;
|
|
irc.bottom -= spaceForwardSize;
|
|
layoutButtons(irc);
|
|
} else {
|
|
// horizontal
|
|
int backbtnpos = rc.left + _btnSize;
|
|
int fwdbtnpos = rc.right - _btnSize;
|
|
r = rc;
|
|
r.right = backbtnpos;
|
|
_btnBack.layout(r);
|
|
r = rc;
|
|
r.left = fwdbtnpos;
|
|
_btnForward.layout(r);
|
|
// indicator
|
|
r = rc;
|
|
r.left = backbtnpos;
|
|
r.right = fwdbtnpos;
|
|
_scrollArea = r;
|
|
int spaceBackSize, spaceForwardSize, indicatorSize;
|
|
bool indicatorVisible = calcButtonSizes(r.width, spaceBackSize, spaceForwardSize, indicatorSize);
|
|
Rect irc = r;
|
|
irc.left += spaceBackSize;
|
|
irc.right -= spaceForwardSize;
|
|
layoutButtons(irc);
|
|
}
|
|
_pos = rc;
|
|
_needLayout = false;
|
|
}
|
|
|
|
override bool onClick(Widget source) {
|
|
Log.d("Scrollbar.onClick ", source.id);
|
|
if (source.compareId("BACK"))
|
|
return sendScrollEvent(ScrollAction.LineUp, position);
|
|
if (source.compareId("FORWARD"))
|
|
return sendScrollEvent(ScrollAction.LineDown, position);
|
|
if (source.compareId("PAGE_UP"))
|
|
return sendScrollEvent(ScrollAction.PageUp, position);
|
|
if (source.compareId("PAGE_DOWN"))
|
|
return sendScrollEvent(ScrollAction.PageDown, position);
|
|
return true;
|
|
}
|
|
|
|
/// 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);
|
|
ClipRectSaver(buf, rc);
|
|
_btnForward.onDraw(buf);
|
|
_btnBack.onDraw(buf);
|
|
_pageUp.onDraw(buf);
|
|
_pageDown.onDraw(buf);
|
|
_indicator.onDraw(buf);
|
|
}
|
|
}
|
|
|
|
class TabItem {
|
|
}
|
|
|
|
class TabWidget {
|
|
|
|
}
|