mirror of https://github.com/buggins/dlangui.git
fix linear layout implementation
This commit is contained in:
parent
3661f7f9fb
commit
1341bb6e71
|
@ -27,9 +27,20 @@ extern (C) int UIAppMain(string[] args) {
|
||||||
|
|
||||||
// create window
|
// create window
|
||||||
Window window = Platform.instance().createWindow("My Window", null);
|
Window window = Platform.instance().createWindow("My Window", null);
|
||||||
Widget myWidget = (new Button()).textColor(0x40FF4000);
|
LinearLayout layout = new LinearLayout();
|
||||||
myWidget.text = "Some strange text string. 1234567890";
|
layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0"));
|
||||||
window.mainWidget = myWidget;
|
layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
|
||||||
|
layout.addChild((new Button()).textColor(0x40FF4000).text("Button1"));
|
||||||
|
layout.addChild((new Button()).textColor(0x000000FF).text("Button2"));
|
||||||
|
layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
|
||||||
|
layout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5)));
|
||||||
|
layout.addChild((new TextWidget()).textColor(0xFF4000).text("Text widget2").padding(Rect(5,5,5,5)).margins(Rect(5,5,5,5)).backgroundColor(0xA0A0A0));
|
||||||
|
layout.addChild((new Button()).textColor(0x000000FF).text("Button3").layoutHeight(FILL_PARENT));
|
||||||
|
layout.addChild((new TextWidget()).textColor(0x004000).text("Text widget3 with very long text"));
|
||||||
|
|
||||||
|
layout.layoutHeight(FILL_PARENT).layoutWidth(FILL_PARENT);
|
||||||
|
|
||||||
|
window.mainWidget = layout;
|
||||||
window.show();
|
window.show();
|
||||||
window.windowCaption = "New Window Caption";
|
window.windowCaption = "New Window Caption";
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
module dlangui.all;
|
module dlangui.all;
|
||||||
|
|
||||||
|
public import dlangui.core.logger;
|
||||||
|
public import dlangui.core.types;
|
||||||
public import dlangui.platforms.common.platform;
|
public import dlangui.platforms.common.platform;
|
||||||
public import dlangui.graphics.images;
|
public import dlangui.graphics.images;
|
||||||
public import dlangui.widgets.widget;
|
public import dlangui.widgets.widget;
|
||||||
public import dlangui.widgets.controls;
|
public import dlangui.widgets.controls;
|
||||||
public import dlangui.core.logger;
|
public import dlangui.widgets.layouts;
|
||||||
public import dlangui.graphics.fonts;
|
public import dlangui.graphics.fonts;
|
||||||
|
|
|
@ -4,16 +4,20 @@ import dlangui.widgets.widget;
|
||||||
|
|
||||||
/// static text widget
|
/// static text widget
|
||||||
class TextWidget : Widget {
|
class TextWidget : Widget {
|
||||||
|
this(string ID = null) {
|
||||||
|
super(ID);
|
||||||
|
styleId = "TEXT";
|
||||||
|
}
|
||||||
protected dstring _text;
|
protected dstring _text;
|
||||||
/// get widget text
|
/// get widget text
|
||||||
override @property dstring text() { return _text; }
|
override @property dstring text() { return _text; }
|
||||||
/// set text to show
|
/// set text to show
|
||||||
override @property void text(dstring s) {
|
override @property Widget text(dstring s) {
|
||||||
_text = s;
|
_text = s;
|
||||||
requestLayout();
|
requestLayout();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
override void measure(int parentWidth, int parentHeight) {
|
override void measure(int parentWidth, int parentHeight) {
|
||||||
_measuredWidth = _measuredHeight = 0;
|
|
||||||
FontRef font = font();
|
FontRef font = font();
|
||||||
Point sz = font.textSize(text);
|
Point sz = font.textSize(text);
|
||||||
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
||||||
|
@ -24,8 +28,8 @@ class TextWidget : Widget {
|
||||||
super.onDraw(buf);
|
super.onDraw(buf);
|
||||||
Rect rc = _pos;
|
Rect rc = _pos;
|
||||||
applyMargins(rc);
|
applyMargins(rc);
|
||||||
applyPadding(rc);
|
|
||||||
ClipRectSaver(buf, rc);
|
ClipRectSaver(buf, rc);
|
||||||
|
applyPadding(rc);
|
||||||
FontRef font = font();
|
FontRef font = font();
|
||||||
Point sz = font.textSize(text);
|
Point sz = font.textSize(text);
|
||||||
applyAlign(rc, sz);
|
applyAlign(rc, sz);
|
||||||
|
@ -39,6 +43,10 @@ class ImageWidget : Widget {
|
||||||
protected string _drawableId;
|
protected string _drawableId;
|
||||||
protected DrawableRef _drawable;
|
protected DrawableRef _drawable;
|
||||||
|
|
||||||
|
this(string ID = null) {
|
||||||
|
super(ID);
|
||||||
|
}
|
||||||
|
|
||||||
/// get drawable image id
|
/// get drawable image id
|
||||||
@property string drawableId() { return _drawableId; }
|
@property string drawableId() { return _drawableId; }
|
||||||
/// set drawable image id
|
/// set drawable image id
|
||||||
|
@ -79,8 +87,8 @@ class ImageWidget : Widget {
|
||||||
super.onDraw(buf);
|
super.onDraw(buf);
|
||||||
Rect rc = _pos;
|
Rect rc = _pos;
|
||||||
applyMargins(rc);
|
applyMargins(rc);
|
||||||
applyPadding(rc);
|
|
||||||
ClipRectSaver(buf, rc);
|
ClipRectSaver(buf, rc);
|
||||||
|
applyPadding(rc);
|
||||||
DrawableRef img = drawable;
|
DrawableRef img = drawable;
|
||||||
if (!img.isNull) {
|
if (!img.isNull) {
|
||||||
Point sz;
|
Point sz;
|
||||||
|
@ -95,16 +103,15 @@ class ImageWidget : Widget {
|
||||||
class Button : Widget {
|
class Button : Widget {
|
||||||
protected dstring _text;
|
protected dstring _text;
|
||||||
override @property dstring text() { return _text; }
|
override @property dstring text() { return _text; }
|
||||||
override @property void text(dstring s) { _text = s; }
|
override @property Widget text(dstring s) { _text = s; requestLayout(); return this; }
|
||||||
this() {
|
this(string ID = null) {
|
||||||
|
super(ID);
|
||||||
styleId = "BUTTON";
|
styleId = "BUTTON";
|
||||||
}
|
}
|
||||||
override void measure(int width, int height) {
|
override void measure(int parentWidth, int parentHeight) {
|
||||||
_measuredWidth = _measuredHeight = 0;
|
FontRef font = font();
|
||||||
}
|
Point sz = font.textSize(text);
|
||||||
override void layout(Rect rc) {
|
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
||||||
_pos = rc;
|
|
||||||
_needLayout = false;
|
|
||||||
}
|
}
|
||||||
override void onDraw(DrawBuf buf) {
|
override void onDraw(DrawBuf buf) {
|
||||||
super.onDraw(buf);
|
super.onDraw(buf);
|
||||||
|
|
|
@ -74,7 +74,6 @@ class LinearLayout : WidgetGroup {
|
||||||
int contentHeight = 0;
|
int contentHeight = 0;
|
||||||
if (orientation == Orientation.Vertical) {
|
if (orientation == Orientation.Vertical) {
|
||||||
// Vertical
|
// Vertical
|
||||||
int position = 0;
|
|
||||||
int totalSize = 0;
|
int totalSize = 0;
|
||||||
int delta = 0;
|
int delta = 0;
|
||||||
int resizableSize = 0;
|
int resizableSize = 0;
|
||||||
|
@ -89,16 +88,17 @@ class LinearLayout : WidgetGroup {
|
||||||
if (item.visibility == Visibility.Gone)
|
if (item.visibility == Visibility.Gone)
|
||||||
continue;
|
continue;
|
||||||
visibleCount++;
|
visibleCount++;
|
||||||
totalSize += item.measuredHeight;
|
int weight = item.layoutWeight;
|
||||||
|
int size = item.measuredHeight;
|
||||||
|
totalSize += size;
|
||||||
if (maxItem < item.measuredWidth)
|
if (maxItem < item.measuredWidth)
|
||||||
maxItem = item.measuredWidth;
|
maxItem = item.measuredWidth;
|
||||||
int weight = item.layoutWeight;
|
|
||||||
if (item.layoutHeight == FILL_PARENT) {
|
if (item.layoutHeight == FILL_PARENT) {
|
||||||
resizableWeight += weight;
|
resizableWeight += weight;
|
||||||
resizableSize += item.measuredHeight;
|
resizableSize += size * weight;
|
||||||
} else {
|
} else {
|
||||||
nonresizableWeight += weight;
|
nonresizableWeight += weight;
|
||||||
nonresizableSize += item.measuredHeight;
|
nonresizableSize += size * weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (layoutWidth == WRAP_CONTENT && maxItem < rc.width)
|
if (layoutWidth == WRAP_CONTENT && maxItem < rc.width)
|
||||||
|
@ -107,42 +107,59 @@ class LinearLayout : WidgetGroup {
|
||||||
contentWidth = rc.width;
|
contentWidth = rc.width;
|
||||||
if (layoutHeight == FILL_PARENT || totalSize > rc.height)
|
if (layoutHeight == FILL_PARENT || totalSize > rc.height)
|
||||||
delta = rc.height - totalSize; // total space to add to fit
|
delta = rc.height - totalSize; // total space to add to fit
|
||||||
|
// calculate resize options and scale
|
||||||
bool needForceResize = false;
|
bool needForceResize = false;
|
||||||
bool needResize = false;
|
bool needResize = false;
|
||||||
int scaleFactor = 10000; // per weight unit
|
int scaleFactor = 10000; // per weight unit
|
||||||
if (delta != 0 && visibleCount > 0) {
|
if (delta != 0 && visibleCount > 0) {
|
||||||
// need resize of some children
|
// need resize of some children
|
||||||
needResize = true;
|
needResize = true;
|
||||||
needForceResize = delta < 0 || resizableSize < delta; // do we need resize non-FILL_PARENT items?
|
// resize all if need to shrink or only resizable are too small to correct delta
|
||||||
|
needForceResize = delta < 0 || resizableWeight == 0; // || resizableSize * 2 / 3 < delta; // do we need resize non-FILL_PARENT items?
|
||||||
|
// calculate scale factor: weight / delta * 10000
|
||||||
if (needForceResize)
|
if (needForceResize)
|
||||||
scaleFactor = 10000 * rc.height / (resizableSize + nonresizableSize) / (nonresizableWeight + resizableWeight);
|
scaleFactor = 10000 * delta / (nonresizableSize + resizableSize);
|
||||||
else
|
else
|
||||||
scaleFactor = 10000 * rc.height / (rc.height - delta) / resizableWeight;
|
scaleFactor = 10000 * delta / resizableSize;
|
||||||
}
|
}
|
||||||
|
//Log.d("VerticalLayout delta=", delta, ", nonres=", nonresizableWeight, ", res=", resizableWeight, ", scale=", scaleFactor);
|
||||||
|
// find last resized - to allow fill space 1 pixel accurate
|
||||||
|
Widget lastResized = null;
|
||||||
for (int i = 0; i < _children.count; i++) {
|
for (int i = 0; i < _children.count; i++) {
|
||||||
Widget item = _children.get(i);
|
Widget item = _children.get(i);
|
||||||
if (item.visibility == Visibility.Gone)
|
if (item.visibility == Visibility.Gone)
|
||||||
continue;
|
continue;
|
||||||
int weight = item.layoutWeight;
|
if (item.layoutHeight == FILL_PARENT || needForceResize) {
|
||||||
if (item.layoutHeight == FILL_PARENT) {
|
lastResized = item;
|
||||||
resizableWeight += weight;
|
|
||||||
resizableSize += item.measuredHeight;
|
|
||||||
} else {
|
|
||||||
nonresizableWeight += weight;
|
|
||||||
nonresizableSize += item.measuredHeight;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// final resize and layout of children
|
||||||
|
int position = 0;
|
||||||
|
int deltaTotal = 0;
|
||||||
for (int i = 0; i < _children.count; i++) {
|
for (int i = 0; i < _children.count; i++) {
|
||||||
Widget item = _children.get(i);
|
Widget item = _children.get(i);
|
||||||
if (item.visibility == Visibility.Gone)
|
if (item.visibility == Visibility.Gone)
|
||||||
continue;
|
continue;
|
||||||
int layoutSize = item.layoutHeight;
|
int layoutSize = item.layoutHeight;
|
||||||
totalWeight += item.measuredHeight;
|
|
||||||
int weight = item.layoutWeight;
|
int weight = item.layoutWeight;
|
||||||
if (layoutSize) {
|
int size = item.measuredHeight;
|
||||||
resizableWeight += weight;
|
if (needResize && (layoutSize == FILL_PARENT || needForceResize)) {
|
||||||
resizableSize += item.measuredHeight;
|
// do resize
|
||||||
|
int correction = scaleFactor * weight * size / 10000;
|
||||||
|
deltaTotal += correction;
|
||||||
|
// for last resized, apply additional correction to resolve calculation inaccuracy
|
||||||
|
if (item == lastResized) {
|
||||||
|
correction += delta - deltaTotal;
|
||||||
}
|
}
|
||||||
|
size += correction;
|
||||||
|
}
|
||||||
|
// apply size
|
||||||
|
Rect childRect = rc;
|
||||||
|
childRect.top += position;
|
||||||
|
childRect.bottom = childRect.top + size;
|
||||||
|
childRect.right = childRect.left + contentWidth;
|
||||||
|
item.layout(childRect);
|
||||||
|
position += size;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Horizontal
|
// Horizontal
|
||||||
|
@ -157,8 +174,13 @@ class LinearLayout : WidgetGroup {
|
||||||
Rect rc = _pos;
|
Rect rc = _pos;
|
||||||
applyMargins(rc);
|
applyMargins(rc);
|
||||||
applyPadding(rc);
|
applyPadding(rc);
|
||||||
// TODO
|
ClipRectSaver(buf, rc);
|
||||||
_needDraw = false;
|
for (int i = 0; i < _children.count; i++) {
|
||||||
|
Widget item = _children.get(i);
|
||||||
|
if (item.visibility != Visibility.Visible)
|
||||||
|
continue;
|
||||||
|
item.onDraw(buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -447,6 +447,29 @@ class Theme : Style {
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================================
|
||||||
|
// override to avoid infinite recursion
|
||||||
|
/// font size
|
||||||
|
@property override string backgroundImageId() const {
|
||||||
|
return _backgroundImageId;
|
||||||
|
}
|
||||||
|
/// minimal width constraint, 0 if limit is not set
|
||||||
|
@property override uint minWidth() const {
|
||||||
|
return _minWidth;
|
||||||
|
}
|
||||||
|
/// max width constraint, returns SIZE_UNSPECIFIED if limit is not set
|
||||||
|
@property override uint maxWidth() const {
|
||||||
|
return _maxWidth;
|
||||||
|
}
|
||||||
|
/// minimal height constraint, 0 if limit is not set
|
||||||
|
@property override uint minHeight() const {
|
||||||
|
return _minHeight;
|
||||||
|
}
|
||||||
|
/// max height constraint, SIZE_UNSPECIFIED if limit is not set
|
||||||
|
@property override uint maxHeight() const {
|
||||||
|
return _maxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
/// create new named style
|
/// create new named style
|
||||||
override Style createSubstyle(string id) {
|
override Style createSubstyle(string id) {
|
||||||
Style style = new Style(this, id);
|
Style style = new Style(this, id);
|
||||||
|
@ -470,6 +493,6 @@ private __gshared Theme _currentTheme;
|
||||||
|
|
||||||
static this() {
|
static this() {
|
||||||
_currentTheme = new Theme("default");
|
_currentTheme = new Theme("default");
|
||||||
Style button = _currentTheme.createSubstyle("BUTTON").backgroundImageId("btn_default_normal");
|
Style button = _currentTheme.createSubstyle("BUTTON").backgroundImageId("btn_default_normal").alignment(Align.Center);
|
||||||
button.alignment(Align.Center);
|
Style text = _currentTheme.createSubstyle("TEXT").margins(Rect(3,3,3,3)).padding(Rect(3,3,3,3));
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,17 +37,16 @@ class Widget {
|
||||||
/// height measured by measure()
|
/// height measured by measure()
|
||||||
protected int _measuredHeight;
|
protected int _measuredHeight;
|
||||||
/// true to force layout
|
/// true to force layout
|
||||||
protected bool _needLayout;
|
protected bool _needLayout = true;
|
||||||
/// true to force redraw
|
/// true to force redraw
|
||||||
protected bool _needDraw;
|
protected bool _needDraw = true;
|
||||||
/// parent widget
|
/// parent widget
|
||||||
protected Widget _parent;
|
protected Widget _parent;
|
||||||
/// window (to be used for top level widgets only!)
|
/// window (to be used for top level widgets only!)
|
||||||
protected Window _window;
|
protected Window _window;
|
||||||
|
|
||||||
this() {
|
this(string ID = null) {
|
||||||
_needLayout = true;
|
_id = id;
|
||||||
_needDraw = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// accessor to style - by lookup in theme by styleId (if style id is not set, theme base style will be used).
|
/// accessor to style - by lookup in theme by styleId (if style id is not set, theme base style will be used).
|
||||||
|
@ -82,7 +81,20 @@ class Widget {
|
||||||
/// set margins for widget - override one from style
|
/// set margins for widget - override one from style
|
||||||
@property Widget margins(Rect rc) { ownStyle.margins = rc; return this; }
|
@property Widget margins(Rect rc) { ownStyle.margins = rc; return this; }
|
||||||
/// get padding (between background bounds and content of widget)
|
/// get padding (between background bounds and content of widget)
|
||||||
@property Rect padding() const { return style.padding; }
|
@property Rect padding() const {
|
||||||
|
// get max padding from style padding and background drawable padding
|
||||||
|
Rect p = style.padding;
|
||||||
|
Rect dp = style.backgroundDrawable.padding;
|
||||||
|
if (p.left < dp.left)
|
||||||
|
p.left = dp.left;
|
||||||
|
if (p.right < dp.right)
|
||||||
|
p.right = dp.right;
|
||||||
|
if (p.top < dp.top)
|
||||||
|
p.top = dp.top;
|
||||||
|
if (p.bottom < dp.bottom)
|
||||||
|
p.bottom = dp.bottom;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
/// set padding for widget - override one from style
|
/// set padding for widget - override one from style
|
||||||
@property Widget padding(Rect rc) { ownStyle.padding = rc; return this; }
|
@property Widget padding(Rect rc) { ownStyle.padding = rc; return this; }
|
||||||
/// returns background color
|
/// returns background color
|
||||||
|
@ -127,7 +139,7 @@ class Widget {
|
||||||
/// returns widget content text (override to support this)
|
/// returns widget content text (override to support this)
|
||||||
@property dstring text() { return ""; }
|
@property dstring text() { return ""; }
|
||||||
/// sets widget content text (override to support this)
|
/// sets widget content text (override to support this)
|
||||||
@property void text(dstring s) { }
|
@property Widget text(dstring s) { return this; }
|
||||||
|
|
||||||
//==================================================================
|
//==================================================================
|
||||||
// Layout and drawing related methods
|
// Layout and drawing related methods
|
||||||
|
|
Loading…
Reference in New Issue