fix linear layout implementation

This commit is contained in:
Vadim Lopatin 2014-03-08 22:09:35 +04:00
parent 3661f7f9fb
commit 1341bb6e71
7 changed files with 218 additions and 141 deletions

View File

@ -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";

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
} }
} }

View File

@ -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));
} }

View File

@ -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