diff --git a/examples/example1/src/main.d b/examples/example1/src/main.d index ba43def4..bbab036d 100644 --- a/examples/example1/src/main.d +++ b/examples/example1/src/main.d @@ -77,6 +77,7 @@ extern (C) int UIAppMain(string[] args) { hlayout.addChild((new ImageWidget()).drawableId("btn_check").padding(Rect(5,5,5,5)).alignment(Align.Center)); hlayout.addChild((new TextWidget()).text("in horizontal layout")); hlayout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5)).alignment(Align.Center)); + hlayout.addChild((new EditLine("editline", "Some text to edit"d))); //hlayout.addChild((new Button()).text(">>")); //.textColor(0x40FF4000) hlayout.backgroundColor = 0x8080C0; layout.addChild(hlayout); diff --git a/res/editbox_background.xml b/res/editbox_background.xml new file mode 100644 index 00000000..976a2127 --- /dev/null +++ b/res/editbox_background.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/res/mdpi/editbox_background_focus_yellow.9.png b/res/mdpi/editbox_background_focus_yellow.9.png new file mode 100644 index 00000000..faf52ede Binary files /dev/null and b/res/mdpi/editbox_background_focus_yellow.9.png differ diff --git a/res/mdpi/editbox_background_normal.9.png b/res/mdpi/editbox_background_normal.9.png new file mode 100644 index 00000000..9b8be770 Binary files /dev/null and b/res/mdpi/editbox_background_normal.9.png differ diff --git a/src/dlangui/all.d b/src/dlangui/all.d index 1f7495be..7a9208d0 100644 --- a/src/dlangui/all.d +++ b/src/dlangui/all.d @@ -59,5 +59,6 @@ public import dlangui.widgets.layouts; public import dlangui.widgets.lists; public import dlangui.widgets.tabs; public import dlangui.widgets.menu; +public import dlangui.widgets.editors; public import dlangui.graphics.fonts; public import dlangui.core.i18n; diff --git a/src/dlangui/widgets/controls.d b/src/dlangui/widgets/controls.d index 3a2620f5..ebe6ad64 100644 --- a/src/dlangui/widgets/controls.d +++ b/src/dlangui/widgets/controls.d @@ -91,12 +91,6 @@ class TextWidget : Widget { 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; diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index da288643..eb423806 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -23,6 +23,66 @@ module dlangui.widgets.editors; import dlangui.widgets.widget; import dlangui.core.signals; +immutable dchar EOL = '\n'; + +/// split dstring by delimiters +dstring[] splitDString(dstring source, dchar delimiter = EOL) { + int start = 0; + dstring[] res; + for (int i = 0; i <= source.length; i++) { + if (i == source.length || source[i] == delimiter) { + if (i >= start) { + dstring line = i > start ? cast(dstring)(source[start .. i].dup) : ""d; + res ~= line; + } + start = i + 1; + } + } + return res; +} + +/// text content position +struct TextPosition { + /// line number, zero based + int line; + /// character position in line (0 == before first character) + int pos; +} + +/// text content range +struct TextRange { + TextPosition start; + TextPosition end; + /// returns true if range is empty + @property bool empty() { + if (start.line > end.line) + return true; + if (start.line < end.line) + return false; + return (start.pos >= end.pos); + } +} + +/// action performed with editable contents +enum EditAction { + /// insert content into specified position (range.start) + Insert, + /// delete content in range + Delete, + /// replace range content with new content + Replace +} + +/// edit operation details for EditableContent +class EditOperation { + /// action performed + EditAction action; + /// range + TextRange range; + /// new content for range (if required for this action) + dstring[] content; +} + /// editable plain text (multiline) class EditableContent { this(bool multiline) { @@ -30,23 +90,26 @@ class EditableContent { _lines.length = 1; // initial state: single empty line } protected bool _multiline; - protected dchar[][] _lines; + protected dstring[] _lines; /// returns all lines concatenated delimited by '\n' @property dstring text() { if (_lines.length == 0) return ""; + if (_lines.length == 1) + return _lines[0]; + // concat lines dchar[] buf; foreach(item;_lines) { if (buf.length) - buf ~= '\n'; + buf ~= EOL; buf ~= item; } return cast(dstring)buf; } + /// replace whole text with another content @property EditableContent text(dstring newContent) { _lines.length = 0; - // TODO: split into lines - _lines ~= newContent.dup; + _lines = splitDString(newContent); return this; } /// returns line text @@ -56,30 +119,58 @@ class EditableContent { } /// returns line text by index dstring line(int index) { - return _lines[index].dup; + return _lines[index]; } } /// single line editor class EditLine : Widget { EditableContent _content; - this(string ID) { + this(string ID, dstring initialContent = null) { super(ID); _content = new EditableContent(false); + styleId = "EDIT_LINE"; + focusable = true; + text = initialContent; } + /// get widget text override @property dstring text() { return _content.text; } + /// set text override @property Widget text(dstring s) { - _content.text = s; + _content.text = s; requestLayout(); return this; } + /// set text override @property Widget text(ref UIString s) { - _content.text = s; + _content.text = s; requestLayout(); return this; } + + /// measure + 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) { + if (visibility != Visibility.Visible) + return; + super.onDraw(buf); + Rect rc = _pos; + applyMargins(rc); + auto saver = ClipRectSaver(buf, rc); + applyPadding(rc); + FontRef font = font(); + dstring txt = text; + Point sz = font.textSize(txt); + applyAlign(rc, sz); + font.drawText(buf, rc.left, rc.top, txt, textColor); + } } diff --git a/src/dlangui/widgets/styles.d b/src/dlangui/widgets/styles.d index 0d5a8d53..f7d500dc 100644 --- a/src/dlangui/widgets/styles.d +++ b/src/dlangui/widgets/styles.d @@ -646,8 +646,8 @@ Theme createDefaultTheme() { res.createSubstyle("BUTTON_LABEL").layoutWidth(FILL_PARENT).alignment(Align.Left|Align.VCenter); res.createSubstyle("BUTTON_ICON").alignment(Align.Center); res.createSubstyle("TEXT").margins(Rect(2,2,2,2)).padding(Rect(1,1,1,1)); - res.createSubstyle("HSPACER").layoutWidth(FILL_PARENT).layoutWeight(100); - res.createSubstyle("VSPACER").layoutHeight(FILL_PARENT).layoutWeight(100); + res.createSubstyle("HSPACER").layoutWidth(FILL_PARENT).minWidth(5).layoutWeight(100); + res.createSubstyle("VSPACER").layoutHeight(FILL_PARENT).minHeight(5).layoutWeight(100); //button.createState(State.Enabled | State.Focused, State.Focused).backgroundImageId("btn_default_small_normal_disable_focused"); //button.createState(State.Enabled, 0).backgroundImageId("btn_default_small_normal_disable"); //button.createState(State.Pressed, State.Pressed).backgroundImageId("btn_default_small_pressed"); @@ -713,6 +713,8 @@ Theme createDefaultTheme() { //listItem.createState(State.Selected, State.Selected).backgroundColor(0xC04040FF).textColor(0x000000); //listItem.createState(State.Enabled, 0).textColor(0x80000000); // half transparent text for disabled item + Style editLine = res.createSubstyle("EDIT_LINE").backgroundImageId("editbox_background").padding(Rect(4,2,4,2)); + return res; }