diff --git a/src/dlangui/graphics/fonts.d b/src/dlangui/graphics/fonts.d index 2313ff8c..bbb224e9 100644 --- a/src/dlangui/graphics/fonts.d +++ b/src/dlangui/graphics/fonts.d @@ -74,11 +74,11 @@ enum FontWeight : int { Bold = 800 } -immutable dchar UNICODE_SOFT_HYPHEN_CODE = 0x00ad; -immutable dchar UNICODE_ZERO_WIDTH_SPACE = 0x200b; -immutable dchar UNICODE_NO_BREAK_SPACE = 0x00a0; -immutable dchar UNICODE_HYPHEN = 0x2010; -immutable dchar UNICODE_NB_HYPHEN = 0x2011; +enum dchar UNICODE_SOFT_HYPHEN_CODE = 0x00ad; +enum dchar UNICODE_ZERO_WIDTH_SPACE = 0x200b; +enum dchar UNICODE_NO_BREAK_SPACE = 0x00a0; +enum dchar UNICODE_HYPHEN = 0x2010; +enum dchar UNICODE_NB_HYPHEN = 0x2011; /// custom character properties - for char-by-char drawing of text string with different character color and style struct CustomCharProps { @@ -124,7 +124,7 @@ static if (ENABLE_OPENGL) { } /// constant for measureText maxWidth paramenter - to tell that all characters of text string should be measured. -immutable int MAX_WIDTH_UNSPECIFIED = int.max; +enum int MAX_WIDTH_UNSPECIFIED = int.max; /** Instance of font with specific size, weight, face, etc. * @@ -322,13 +322,20 @@ class Font : RefCountedObject { * tabOffset = when string is drawn not from left position, use to move tab stops left/right * textFlags = TextFlag bit set - to control underline, hotkey label processing, etc... ************************************************************************/ - Point textSize(const dchar[] text, int maxWidth = MAX_WIDTH_UNSPECIFIED, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) { - if (_textSizeBuffer.length < text.length + 1) - _textSizeBuffer.length = text.length + 1; - int charsMeasured = measureText(text, _textSizeBuffer, maxWidth, tabSize, tabOffset, textFlags); + Point textSize(dstring text, int maxWidth = MAX_WIDTH_UNSPECIFIED, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) { + return textSizeMemoized(this, text, maxWidth, tabSize, tabOffset, textFlags); + } + + import std.functional; + alias textSizeMemoized = memoize!(Font.textSizeImpl); + + static Point textSizeImpl(Font font, const dchar[] text, int maxWidth = MAX_WIDTH_UNSPECIFIED, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) { + if (font._textSizeBuffer.length < text.length + 1) + font._textSizeBuffer.length = text.length + 1; + int charsMeasured = font.measureText(text, font._textSizeBuffer, maxWidth, tabSize, tabOffset, textFlags); if (charsMeasured < 1) return Point(0,0); - return Point(_textSizeBuffer[charsMeasured - 1], height); + return Point(font._textSizeBuffer[charsMeasured - 1], font.height); } /***************************************************************************************** diff --git a/src/dlangui/graphics/glsupport.d b/src/dlangui/graphics/glsupport.d index f1a4f387..43c7df76 100644 --- a/src/dlangui/graphics/glsupport.d +++ b/src/dlangui/graphics/glsupport.d @@ -622,7 +622,11 @@ private void FillColor(uint color, Color[] buf_slice) { } } -private float[] convertColors(uint[] cols) pure nothrow { + +import std.functional; +alias convertColors = memoize!(convertColorsImpl); + +float[] convertColorsImpl(uint[] cols) pure nothrow { float[] colors; colors.length = cols.length * 4; foreach(i; 0 .. cols.length) { diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 6a25d80b..8da2b104 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -410,7 +410,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction foreach(ref ch; s) ch = '9'; FontRef fnt = font; - Point sz = fnt.textSize(s); + Point sz = fnt.textSize(cast(immutable)s); _lineNumbersWidth = sz.x; } _leftPaneWidth = _lineNumbersWidth + _modificationMarksWidth + _foldingWidth + _iconsWidth; @@ -765,7 +765,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction return _wantTabs; } - /// sets tab size (in number of spaces) + /// ditto @property EditWidgetBase wantTabs(bool wantTabs) { _wantTabs = wantTabs; return this; @@ -1884,7 +1884,6 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [event.text]); _content.performOperation(op, this); } - if (focused) startCaretBlinking(); return true; } } @@ -1894,9 +1893,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction //if (event.keyCode == KeyCode.RETURN && !readOnly && !_content.multiline) { // return true; //} - bool res = super.onKeyEvent(event); - //if (focused) startCaretBlinking(); - return res; + return super.onKeyEvent(event); } /// Handle Ctrl + Left mouse click on text @@ -2166,6 +2163,9 @@ class EditLine : EditWidgetBase { /// measure override void measure(int parentWidth, int parentHeight) { + if (visibility == Visibility.Gone) + return; + updateFontProps(); measureVisibleText(); measureTextToSetWidgetSize(); @@ -2242,11 +2242,9 @@ class EditLine : EditWidgetBase { // line inside selection Rect startrc = textPosToClient(_selectionRange.start); Rect endrc = textPosToClient(_selectionRange.end); - int startx = startrc.left + _clientRect.left; - int endx = endrc.left + _clientRect.left; Rect rc = lineRect; - rc.left = startx; - rc.right = endx; + rc.left = startrc.left + _clientRect.left; + rc.right = endrc.left + _clientRect.left; if (!rc.empty) { // draw selection rect for line buf.fillRect(rc, focused ? _selectionColorFocused : _selectionColorNormal); @@ -2269,17 +2267,11 @@ class EditLine : EditWidgetBase { applyMargins(rc); applyPadding(rc); auto saver = ClipRectSaver(buf, rc, alpha); + FontRef font = font(); dstring txt = applyPasswordChar(text); - Point sz = font.textSize(txt); - //applyAlign(rc, sz); - Rect lineRect = _clientRect; - lineRect.left = _clientRect.left - _scrollPos.x; - lineRect.right = lineRect.left + calcLineWidth(txt); - Rect visibleRect = lineRect; - visibleRect.left = _clientRect.left; - visibleRect.right = _clientRect.right; - drawLineBackground(buf, lineRect, visibleRect); + + drawLineBackground(buf, _clientRect, _clientRect); font.drawText(buf, rc.left - _scrollPos.x, rc.top, txt, textColor, tabSize); drawCaret(buf); @@ -2366,9 +2358,9 @@ class EditBox : EditWidgetBase { /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). override void layout(Rect rc) { - if (visibility == Visibility.Gone) { + if (visibility == Visibility.Gone) return; - } + if (rc != _pos) _contentChanged = true; Rect contentRc = rc; @@ -2928,9 +2920,9 @@ class EditBox : EditWidgetBase { /// measure override void measure(int parentWidth, int parentHeight) { - if (visibility == Visibility.Gone) { + if (visibility == Visibility.Gone) return; - } + updateFontProps(); updateMaxLineWidth(); int findPanelHeight; @@ -3341,7 +3333,7 @@ class EditBox : EditWidgetBase { FontRef font = font(); for (int i = 0; i < _visibleLines.length; i++) { dstring txt = _visibleLines[i]; - Rect lineRect = rc; + Rect lineRect; lineRect.left = _clientRect.left - _scrollPos.x; lineRect.right = lineRect.left + calcLineWidth(_content[_firstVisibleLine + i]); lineRect.top = _clientRect.top + i * _lineHeight; @@ -3558,9 +3550,9 @@ class LogWidget : EditBox { /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). override void layout(Rect rc) { - if (visibility == Visibility.Gone) { + if (visibility == Visibility.Gone) return; - } + super.layout(rc); if (_scrollLock) { measureVisibleText(); diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d index 4ff73e09..c0d020bd 100644 --- a/src/dlangui/widgets/tabs.d +++ b/src/dlangui/widgets/tabs.d @@ -53,6 +53,7 @@ class TabItem { private UIString _label; private UIString _tooltipText; private long _lastAccessTs; + this(string id, string labelRes, string iconRes = null, dstring tooltipText = null) { _id = id; _label.id = labelRes; @@ -73,6 +74,7 @@ class TabItem { _lastAccessTs = _lastAccessCounter++; _tooltipText = UIString.fromRaw(tooltipText); } + @property string iconId() const { return _iconRes; } @property string id() const { return _id; } @property ref UIString text() { return _label; } @@ -83,6 +85,7 @@ class TabItem { void updateAccessTs() { _lastAccessTs = _lastAccessCounter++; //std.datetime.Clock.currStdTime; } + /// tooltip text @property dstring tooltipText() { if (_tooltipText.empty) @@ -123,6 +126,7 @@ class TabItemWidget : HorizontalLayout { Signal!TabCloseHandler tabClose; @property TabItem tabItem() { return _item; } @property TabControl tabControl() { return cast(TabControl)parent; } + this(TabItem item, bool enableCloseButton = true) { styleId = STYLE_TAB_UP_BUTTON; _enableCloseButton = enableCloseButton; @@ -153,6 +157,7 @@ class TabItemWidget : HorizontalLayout { if (_closeButton) _closeButton.tooltipText = _item.tooltipText; } + /// tooltip text - when not empty, widget will show tooltips automatically; for advanced tooltips - override hasTooltip and createTooltip override @property dstring tooltipText() { return _item.tooltipText; } /// tooltip text - when not empty, widget will show tooltips automatically; for advanced tooltips - override hasTooltip and createTooltip @@ -180,10 +185,12 @@ class TabItemWidget : HorizontalLayout { styleId = tabButtonStyle; _label.styleId = tabButtonTextStyle; } + override void onDraw(DrawBuf buf) { //debug Log.d("TabWidget.onDraw ", id); super.onDraw(buf); } + protected bool onClick(Widget source) { if (source.compareId("CLOSE")) { Log.d("tab close button pressed"); @@ -192,6 +199,7 @@ class TabItemWidget : HorizontalLayout { } return true; } + @property TabItem item() { return _item; } @@ -334,6 +342,7 @@ class TabControl : WidgetGroupDefaultDrawing { styleId = _tabStyle; addChild(_moreButton); // first child is always MORE button, the rest corresponds to tab list } + void setStyles(string tabStyle, string tabButtonStyle, string tabButtonTextStyle) { _tabStyle = tabStyle; _tabButtonStyle = tabButtonStyle; @@ -533,6 +542,7 @@ class TabControl : WidgetGroupDefaultDrawing { TabItem item = new TabItem(id, label, iconId, tooltipText); return addTab(item, -1, enableCloseButton); } + protected MenuItem getMoreButtonPopupMenu() { if (moreButtonPopupMenu.assigned) { if (auto menu = moreButtonPopupMenu(this)) { @@ -553,6 +563,7 @@ class TabControl : WidgetGroupDefaultDrawing { } return null; } + /// try to invoke popup menu, return true if popup menu is shown protected bool handleMorePopupMenu() { if (auto menu = getMoreButtonPopupMenu()) { @@ -567,6 +578,7 @@ class TabControl : WidgetGroupDefaultDrawing { } return false; } + /// override to handle specific actions override bool handleAction(const Action a) { if (a.id == StandardAction.TabSelectItem) { @@ -575,6 +587,7 @@ class TabControl : WidgetGroupDefaultDrawing { } return super.handleAction(a); } + protected bool onMouse(Widget source, MouseEvent event) { if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) { if (source.compareId("MORE")) { @@ -594,6 +607,7 @@ class TabControl : WidgetGroupDefaultDrawing { } return true; } + /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). override void measure(int parentWidth, int parentHeight) { //Log.d("tabControl.measure enter"); @@ -627,6 +641,7 @@ class TabControl : WidgetGroupDefaultDrawing { measuredContent(parentWidth, parentHeight, sz.x, sz.y); //Log.d("tabControl.measure exit"); } + /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). override void layout(Rect rc) { //Log.d("tabControl.layout enter"); @@ -736,7 +751,6 @@ class TabControl : WidgetGroupDefaultDrawing { if (tabChanged.assigned) tabChanged(_selectedTabId, previousSelectedTab); } - } /// container for widgets controlled by TabControl @@ -799,6 +813,7 @@ class TabHost : FrameLayout, TabHandler { requestLayout(); return this; } + /// add new tab by id and label string TabHost addTab(Widget widget, dstring label, string iconId = null, bool enableCloseButton = false, dstring tooltipText = null) { assert(_tabControl !is null, "No TabControl set for TabHost"); @@ -810,6 +825,7 @@ class TabHost : FrameLayout, TabHandler { addChild(widget); return this; } + /// add new tab by id and label string resource id TabHost addTab(Widget widget, string labelResourceId, string iconId = null, bool enableCloseButton = false, dstring tooltipText = null) { assert(_tabControl !is null, "No TabControl set for TabHost"); diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index b8f15bcc..e769f82a 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -366,7 +366,7 @@ public: requestLayout(); return this; } - immutable static int FOCUS_RECT_PADDING = 2; + static enum FOCUS_RECT_PADDING = 2; /// get padding (between background bounds and content of widget) @property Rect padding() const { // get max padding from style padding and background drawable padding @@ -640,16 +640,16 @@ public: /// returns widget visibility (Visible, Invisible, Gone) @property Visibility visibility() { return _visibility; } /// sets widget visibility (Visible, Invisible, Gone) - @property Widget visibility(Visibility visible) { - if (_visibility != visible) { - if ((_visibility == Visibility.Gone) || (visible == Visibility.Gone)) { + @property Widget visibility(Visibility newVisibility) { + if (_visibility != newVisibility) { + if ((_visibility == Visibility.Gone) || (newVisibility == Visibility.Gone)) { if (parent) parent.requestLayout(); else requestLayout(); } else invalidate(); - _visibility = visible; + _visibility = newVisibility; } return this; } @@ -868,9 +868,9 @@ public: this.rect = widget.pos; } static if (BACKEND_GUI) { - static immutable int NEAR_THRESHOLD = 10; + static enum NEAR_THRESHOLD = 10; } else { - static immutable int NEAR_THRESHOLD = 1; + static enum NEAR_THRESHOLD = 1; } bool nearX(TabOrderInfo v) { return v.rect.left >= rect.left - NEAR_THRESHOLD && v.rect.left <= rect.left + NEAR_THRESHOLD;