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

@ -1,12 +1,12 @@
module winmain;
import dlangui.all;
import std.stdio;
/// workaround for link issue when WinMain is located in library
version(Windows) {
import win32.windows;
import dlangui.platforms.windows.winapp;
module winmain;
import dlangui.all;
import std.stdio;
/// workaround for link issue when WinMain is located in library
version(Windows) {
import win32.windows;
import dlangui.platforms.windows.winapp;
extern (Windows)
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
@ -15,24 +15,35 @@ version(Windows) {
lpCmdLine, nCmdShow);
}
}
/// entry point for dlangui based application
extern (C) int UIAppMain(string[] args) {
// setup resource dir
string resourceDir = exePath() ~ "..\\res\\";
string[] imageDirs = [
resourceDir
];
drawableCache.resourcePaths = imageDirs;
// create window
Window window = Platform.instance().createWindow("My Window", null);
Widget myWidget = (new Button()).textColor(0x40FF4000);
myWidget.text = "Some strange text string. 1234567890";
window.mainWidget = myWidget;
window.show();
window.windowCaption = "New Window Caption";
// run message loop
return Platform.instance().enterMessageLoop();
}
/// entry point for dlangui based application
extern (C) int UIAppMain(string[] args) {
// setup resource dir
string resourceDir = exePath() ~ "..\\res\\";
string[] imageDirs = [
resourceDir
];
drawableCache.resourcePaths = imageDirs;
// create window
Window window = Platform.instance().createWindow("My Window", null);
LinearLayout layout = new LinearLayout();
layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0"));
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.windowCaption = "New Window Caption";
// run message loop
return Platform.instance().enterMessageLoop();
}

View File

@ -1,8 +1,10 @@
module dlangui.all;
public import dlangui.core.logger;
public import dlangui.core.types;
public import dlangui.platforms.common.platform;
public import dlangui.graphics.images;
public import dlangui.widgets.widget;
public import dlangui.widgets.controls;
public import dlangui.core.logger;
public import dlangui.widgets.layouts;
public import dlangui.graphics.fonts;

View File

@ -1,68 +1,68 @@
module dlangui.platforms.common.platform;
import dlangui.widgets.widget;
import dlangui.graphics.drawbuf;
import std.file;
public class Window {
int _dx;
int _dy;
Widget _mainWidget;
public @property int width() { return _dx; }
public @property int height() { return _dy; }
public @property Widget mainWidget() { return _mainWidget; }
public @property void mainWidget(Widget widget) {
if (_mainWidget !is null)
_mainWidget.window = null;
_mainWidget = widget;
if (_mainWidget !is null)
_mainWidget.window = this;
}
abstract public void show();
abstract public @property string windowCaption();
abstract public @property void windowCaption(string caption);
public void onResize(int width, int height) {
if (_dx == width && _dy == height)
return;
_dx = width;
_dy = height;
if (_mainWidget !is null) {
_mainWidget.measure(_dx, _dy);
_mainWidget.layout(Rect(0, 0, _dx, _dy));
}
}
public void onDraw(DrawBuf buf) {
if (_mainWidget !is null) {
_mainWidget.onDraw(buf);
}
}
}
public class Platform {
static __gshared Platform _instance;
public static void setInstance(Platform instance) {
_instance = instance;
}
public static Platform instance() {
return _instance;
}
abstract public Window createWindow(string windowCaption, Window parent);
abstract public int enterMessageLoop();
}
version (Windows) {
immutable char PATH_DELIMITER = '\\';
}
version (Unix) {
immutable char PATH_DELIMITER = '/';
}
module dlangui.platforms.common.platform;
import dlangui.widgets.widget;
import dlangui.graphics.drawbuf;
import std.file;
public class Window {
int _dx;
int _dy;
Widget _mainWidget;
public @property int width() { return _dx; }
public @property int height() { return _dy; }
public @property Widget mainWidget() { return _mainWidget; }
public @property void mainWidget(Widget widget) {
if (_mainWidget !is null)
_mainWidget.window = null;
_mainWidget = widget;
if (_mainWidget !is null)
_mainWidget.window = this;
}
abstract public void show();
abstract public @property string windowCaption();
abstract public @property void windowCaption(string caption);
public void onResize(int width, int height) {
if (_dx == width && _dy == height)
return;
_dx = width;
_dy = height;
if (_mainWidget !is null) {
_mainWidget.measure(_dx, _dy);
_mainWidget.layout(Rect(0, 0, _dx, _dy));
}
}
public void onDraw(DrawBuf buf) {
if (_mainWidget !is null) {
_mainWidget.onDraw(buf);
}
}
}
public class Platform {
static __gshared Platform _instance;
public static void setInstance(Platform instance) {
_instance = instance;
}
public static Platform instance() {
return _instance;
}
abstract public Window createWindow(string windowCaption, Window parent);
abstract public int enterMessageLoop();
}
version (Windows) {
immutable char PATH_DELIMITER = '\\';
}
version (Unix) {
immutable char PATH_DELIMITER = '/';
}
/// returns current executable path only, including last path delimiter
string exePath() {
string path = thisExePath();
int lastSlash = 0;
for (int i = 0; i < path.length; i++)
if (path[i] == PATH_DELIMITER)
lastSlash = i;
return path[0 .. lastSlash + 1];
}
string exePath() {
string path = thisExePath();
int lastSlash = 0;
for (int i = 0; i < path.length; i++)
if (path[i] == PATH_DELIMITER)
lastSlash = i;
return path[0 .. lastSlash + 1];
}

View File

@ -4,16 +4,20 @@ import dlangui.widgets.widget;
/// static text widget
class TextWidget : Widget {
this(string ID = null) {
super(ID);
styleId = "TEXT";
}
protected dstring _text;
/// get widget text
override @property dstring text() { return _text; }
/// set text to show
override @property void text(dstring s) {
override @property Widget text(dstring s) {
_text = s;
requestLayout();
return this;
}
override void measure(int parentWidth, int parentHeight) {
_measuredWidth = _measuredHeight = 0;
FontRef font = font();
Point sz = font.textSize(text);
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
@ -24,8 +28,8 @@ class TextWidget : Widget {
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
ClipRectSaver(buf, rc);
applyPadding(rc);
FontRef font = font();
Point sz = font.textSize(text);
applyAlign(rc, sz);
@ -39,6 +43,10 @@ class ImageWidget : Widget {
protected string _drawableId;
protected DrawableRef _drawable;
this(string ID = null) {
super(ID);
}
/// get drawable image id
@property string drawableId() { return _drawableId; }
/// set drawable image id
@ -79,8 +87,8 @@ class ImageWidget : Widget {
super.onDraw(buf);
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
ClipRectSaver(buf, rc);
applyPadding(rc);
DrawableRef img = drawable;
if (!img.isNull) {
Point sz;
@ -95,16 +103,15 @@ class ImageWidget : Widget {
class Button : Widget {
protected dstring _text;
override @property dstring text() { return _text; }
override @property void text(dstring s) { _text = s; }
this() {
override @property Widget text(dstring s) { _text = s; requestLayout(); return this; }
this(string ID = null) {
super(ID);
styleId = "BUTTON";
}
override void measure(int width, int height) {
_measuredWidth = _measuredHeight = 0;
}
override void layout(Rect rc) {
_pos = rc;
_needLayout = false;
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);

View File

@ -74,7 +74,6 @@ class LinearLayout : WidgetGroup {
int contentHeight = 0;
if (orientation == Orientation.Vertical) {
// Vertical
int position = 0;
int totalSize = 0;
int delta = 0;
int resizableSize = 0;
@ -89,16 +88,17 @@ class LinearLayout : WidgetGroup {
if (item.visibility == Visibility.Gone)
continue;
visibleCount++;
totalSize += item.measuredHeight;
int weight = item.layoutWeight;
int size = item.measuredHeight;
totalSize += size;
if (maxItem < item.measuredWidth)
maxItem = item.measuredWidth;
int weight = item.layoutWeight;
if (item.layoutHeight == FILL_PARENT) {
resizableWeight += weight;
resizableSize += item.measuredHeight;
resizableSize += size * weight;
} else {
nonresizableWeight += weight;
nonresizableSize += item.measuredHeight;
nonresizableSize += size * weight;
}
}
if (layoutWidth == WRAP_CONTENT && maxItem < rc.width)
@ -107,42 +107,59 @@ class LinearLayout : WidgetGroup {
contentWidth = rc.width;
if (layoutHeight == FILL_PARENT || totalSize > rc.height)
delta = rc.height - totalSize; // total space to add to fit
// calculate resize options and scale
bool needForceResize = false;
bool needResize = false;
int scaleFactor = 10000; // per weight unit
if (delta != 0 && visibleCount > 0) {
// need resize of some children
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)
scaleFactor = 10000 * rc.height / (resizableSize + nonresizableSize) / (nonresizableWeight + resizableWeight);
scaleFactor = 10000 * delta / (nonresizableSize + resizableSize);
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++) {
Widget item = _children.get(i);
if (item.visibility == Visibility.Gone)
continue;
int weight = item.layoutWeight;
if (item.layoutHeight == FILL_PARENT) {
resizableWeight += weight;
resizableSize += item.measuredHeight;
} else {
nonresizableWeight += weight;
nonresizableSize += item.measuredHeight;
if (item.layoutHeight == FILL_PARENT || needForceResize) {
lastResized = item;
}
}
}
// final resize and layout of children
int position = 0;
int deltaTotal = 0;
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
if (item.visibility == Visibility.Gone)
continue;
int layoutSize = item.layoutHeight;
totalWeight += item.measuredHeight;
int weight = item.layoutWeight;
if (layoutSize) {
resizableWeight += weight;
resizableSize += item.measuredHeight;
int size = item.measuredHeight;
if (needResize && (layoutSize == FILL_PARENT || needForceResize)) {
// 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 {
// Horizontal
@ -157,8 +174,13 @@ class LinearLayout : WidgetGroup {
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
// TODO
_needDraw = false;
ClipRectSaver(buf, rc);
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;
}
// ================================================
// 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
override Style createSubstyle(string id) {
Style style = new Style(this, id);
@ -470,6 +493,6 @@ private __gshared Theme _currentTheme;
static this() {
_currentTheme = new Theme("default");
Style button = _currentTheme.createSubstyle("BUTTON").backgroundImageId("btn_default_normal");
button.alignment(Align.Center);
Style button = _currentTheme.createSubstyle("BUTTON").backgroundImageId("btn_default_normal").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()
protected int _measuredHeight;
/// true to force layout
protected bool _needLayout;
protected bool _needLayout = true;
/// true to force redraw
protected bool _needDraw;
protected bool _needDraw = true;
/// parent widget
protected Widget _parent;
/// window (to be used for top level widgets only!)
protected Window _window;
this() {
_needLayout = true;
_needDraw = true;
this(string ID = null) {
_id = id;
}
/// 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
@property Widget margins(Rect rc) { ownStyle.margins = rc; return this; }
/// 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
@property Widget padding(Rect rc) { ownStyle.padding = rc; return this; }
/// returns background color
@ -127,7 +139,7 @@ class Widget {
/// returns widget content text (override to support this)
@property dstring text() { return ""; }
/// 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