Merge pull request #491 from dayllenger/memoize

Powerful optimization: memoize
This commit is contained in:
Vadim Lopatin 2017-10-16 13:27:29 +03:00 committed by GitHub
commit 33a7501bb9
5 changed files with 65 additions and 46 deletions

View File

@ -74,11 +74,11 @@ enum FontWeight : int {
Bold = 800 Bold = 800
} }
immutable dchar UNICODE_SOFT_HYPHEN_CODE = 0x00ad; enum dchar UNICODE_SOFT_HYPHEN_CODE = 0x00ad;
immutable dchar UNICODE_ZERO_WIDTH_SPACE = 0x200b; enum dchar UNICODE_ZERO_WIDTH_SPACE = 0x200b;
immutable dchar UNICODE_NO_BREAK_SPACE = 0x00a0; enum dchar UNICODE_NO_BREAK_SPACE = 0x00a0;
immutable dchar UNICODE_HYPHEN = 0x2010; enum dchar UNICODE_HYPHEN = 0x2010;
immutable dchar UNICODE_NB_HYPHEN = 0x2011; enum dchar UNICODE_NB_HYPHEN = 0x2011;
/// custom character properties - for char-by-char drawing of text string with different character color and style /// custom character properties - for char-by-char drawing of text string with different character color and style
struct CustomCharProps { 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. /// 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. /** 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 * 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... * 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) { Point textSize(dstring text, int maxWidth = MAX_WIDTH_UNSPECIFIED, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
if (_textSizeBuffer.length < text.length + 1) return textSizeMemoized(this, text, maxWidth, tabSize, tabOffset, textFlags);
_textSizeBuffer.length = text.length + 1; }
int charsMeasured = measureText(text, _textSizeBuffer, 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) if (charsMeasured < 1)
return Point(0,0); return Point(0,0);
return Point(_textSizeBuffer[charsMeasured - 1], height); return Point(font._textSizeBuffer[charsMeasured - 1], font.height);
} }
/***************************************************************************************** /*****************************************************************************************

View File

@ -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; float[] colors;
colors.length = cols.length * 4; colors.length = cols.length * 4;
foreach(i; 0 .. cols.length) { foreach(i; 0 .. cols.length) {

View File

@ -410,7 +410,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
foreach(ref ch; s) foreach(ref ch; s)
ch = '9'; ch = '9';
FontRef fnt = font; FontRef fnt = font;
Point sz = fnt.textSize(s); Point sz = fnt.textSize(cast(immutable)s);
_lineNumbersWidth = sz.x; _lineNumbersWidth = sz.x;
} }
_leftPaneWidth = _lineNumbersWidth + _modificationMarksWidth + _foldingWidth + _iconsWidth; _leftPaneWidth = _lineNumbersWidth + _modificationMarksWidth + _foldingWidth + _iconsWidth;
@ -765,7 +765,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
return _wantTabs; return _wantTabs;
} }
/// sets tab size (in number of spaces) /// ditto
@property EditWidgetBase wantTabs(bool wantTabs) { @property EditWidgetBase wantTabs(bool wantTabs) {
_wantTabs = wantTabs; _wantTabs = wantTabs;
return this; return this;
@ -1884,7 +1884,6 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [event.text]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [event.text]);
_content.performOperation(op, this); _content.performOperation(op, this);
} }
if (focused) startCaretBlinking();
return true; return true;
} }
} }
@ -1894,9 +1893,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
//if (event.keyCode == KeyCode.RETURN && !readOnly && !_content.multiline) { //if (event.keyCode == KeyCode.RETURN && !readOnly && !_content.multiline) {
// return true; // return true;
//} //}
bool res = super.onKeyEvent(event); return super.onKeyEvent(event);
//if (focused) startCaretBlinking();
return res;
} }
/// Handle Ctrl + Left mouse click on text /// Handle Ctrl + Left mouse click on text
@ -2166,6 +2163,9 @@ class EditLine : EditWidgetBase {
/// measure /// measure
override void measure(int parentWidth, int parentHeight) { override void measure(int parentWidth, int parentHeight) {
if (visibility == Visibility.Gone)
return;
updateFontProps(); updateFontProps();
measureVisibleText(); measureVisibleText();
measureTextToSetWidgetSize(); measureTextToSetWidgetSize();
@ -2242,11 +2242,9 @@ class EditLine : EditWidgetBase {
// line inside selection // line inside selection
Rect startrc = textPosToClient(_selectionRange.start); Rect startrc = textPosToClient(_selectionRange.start);
Rect endrc = textPosToClient(_selectionRange.end); Rect endrc = textPosToClient(_selectionRange.end);
int startx = startrc.left + _clientRect.left;
int endx = endrc.left + _clientRect.left;
Rect rc = lineRect; Rect rc = lineRect;
rc.left = startx; rc.left = startrc.left + _clientRect.left;
rc.right = endx; rc.right = endrc.left + _clientRect.left;
if (!rc.empty) { if (!rc.empty) {
// draw selection rect for line // draw selection rect for line
buf.fillRect(rc, focused ? _selectionColorFocused : _selectionColorNormal); buf.fillRect(rc, focused ? _selectionColorFocused : _selectionColorNormal);
@ -2269,17 +2267,11 @@ class EditLine : EditWidgetBase {
applyMargins(rc); applyMargins(rc);
applyPadding(rc); applyPadding(rc);
auto saver = ClipRectSaver(buf, rc, alpha); auto saver = ClipRectSaver(buf, rc, alpha);
FontRef font = font(); FontRef font = font();
dstring txt = applyPasswordChar(text); dstring txt = applyPasswordChar(text);
Point sz = font.textSize(txt);
//applyAlign(rc, sz); drawLineBackground(buf, _clientRect, _clientRect);
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);
font.drawText(buf, rc.left - _scrollPos.x, rc.top, txt, textColor, tabSize); font.drawText(buf, rc.left - _scrollPos.x, rc.top, txt, textColor, tabSize);
drawCaret(buf); 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). /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
override void layout(Rect rc) { override void layout(Rect rc) {
if (visibility == Visibility.Gone) { if (visibility == Visibility.Gone)
return; return;
}
if (rc != _pos) if (rc != _pos)
_contentChanged = true; _contentChanged = true;
Rect contentRc = rc; Rect contentRc = rc;
@ -2928,9 +2920,9 @@ class EditBox : EditWidgetBase {
/// measure /// measure
override void measure(int parentWidth, int parentHeight) { override void measure(int parentWidth, int parentHeight) {
if (visibility == Visibility.Gone) { if (visibility == Visibility.Gone)
return; return;
}
updateFontProps(); updateFontProps();
updateMaxLineWidth(); updateMaxLineWidth();
int findPanelHeight; int findPanelHeight;
@ -3341,7 +3333,7 @@ class EditBox : EditWidgetBase {
FontRef font = font(); FontRef font = font();
for (int i = 0; i < _visibleLines.length; i++) { for (int i = 0; i < _visibleLines.length; i++) {
dstring txt = _visibleLines[i]; dstring txt = _visibleLines[i];
Rect lineRect = rc; Rect lineRect;
lineRect.left = _clientRect.left - _scrollPos.x; lineRect.left = _clientRect.left - _scrollPos.x;
lineRect.right = lineRect.left + calcLineWidth(_content[_firstVisibleLine + i]); lineRect.right = lineRect.left + calcLineWidth(_content[_firstVisibleLine + i]);
lineRect.top = _clientRect.top + i * _lineHeight; 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). /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
override void layout(Rect rc) { override void layout(Rect rc) {
if (visibility == Visibility.Gone) { if (visibility == Visibility.Gone)
return; return;
}
super.layout(rc); super.layout(rc);
if (_scrollLock) { if (_scrollLock) {
measureVisibleText(); measureVisibleText();

View File

@ -53,6 +53,7 @@ class TabItem {
private UIString _label; private UIString _label;
private UIString _tooltipText; private UIString _tooltipText;
private long _lastAccessTs; private long _lastAccessTs;
this(string id, string labelRes, string iconRes = null, dstring tooltipText = null) { this(string id, string labelRes, string iconRes = null, dstring tooltipText = null) {
_id = id; _id = id;
_label.id = labelRes; _label.id = labelRes;
@ -73,6 +74,7 @@ class TabItem {
_lastAccessTs = _lastAccessCounter++; _lastAccessTs = _lastAccessCounter++;
_tooltipText = UIString.fromRaw(tooltipText); _tooltipText = UIString.fromRaw(tooltipText);
} }
@property string iconId() const { return _iconRes; } @property string iconId() const { return _iconRes; }
@property string id() const { return _id; } @property string id() const { return _id; }
@property ref UIString text() { return _label; } @property ref UIString text() { return _label; }
@ -83,6 +85,7 @@ class TabItem {
void updateAccessTs() { void updateAccessTs() {
_lastAccessTs = _lastAccessCounter++; //std.datetime.Clock.currStdTime; _lastAccessTs = _lastAccessCounter++; //std.datetime.Clock.currStdTime;
} }
/// tooltip text /// tooltip text
@property dstring tooltipText() { @property dstring tooltipText() {
if (_tooltipText.empty) if (_tooltipText.empty)
@ -123,6 +126,7 @@ class TabItemWidget : HorizontalLayout {
Signal!TabCloseHandler tabClose; Signal!TabCloseHandler tabClose;
@property TabItem tabItem() { return _item; } @property TabItem tabItem() { return _item; }
@property TabControl tabControl() { return cast(TabControl)parent; } @property TabControl tabControl() { return cast(TabControl)parent; }
this(TabItem item, bool enableCloseButton = true) { this(TabItem item, bool enableCloseButton = true) {
styleId = STYLE_TAB_UP_BUTTON; styleId = STYLE_TAB_UP_BUTTON;
_enableCloseButton = enableCloseButton; _enableCloseButton = enableCloseButton;
@ -153,6 +157,7 @@ class TabItemWidget : HorizontalLayout {
if (_closeButton) if (_closeButton)
_closeButton.tooltipText = _item.tooltipText; _closeButton.tooltipText = _item.tooltipText;
} }
/// tooltip text - when not empty, widget will show tooltips automatically; for advanced tooltips - override hasTooltip and createTooltip /// tooltip text - when not empty, widget will show tooltips automatically; for advanced tooltips - override hasTooltip and createTooltip
override @property dstring tooltipText() { return _item.tooltipText; } override @property dstring tooltipText() { return _item.tooltipText; }
/// tooltip text - when not empty, widget will show tooltips automatically; for advanced tooltips - override hasTooltip and createTooltip /// 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; styleId = tabButtonStyle;
_label.styleId = tabButtonTextStyle; _label.styleId = tabButtonTextStyle;
} }
override void onDraw(DrawBuf buf) { override void onDraw(DrawBuf buf) {
//debug Log.d("TabWidget.onDraw ", id); //debug Log.d("TabWidget.onDraw ", id);
super.onDraw(buf); super.onDraw(buf);
} }
protected bool onClick(Widget source) { protected bool onClick(Widget source) {
if (source.compareId("CLOSE")) { if (source.compareId("CLOSE")) {
Log.d("tab close button pressed"); Log.d("tab close button pressed");
@ -192,6 +199,7 @@ class TabItemWidget : HorizontalLayout {
} }
return true; return true;
} }
@property TabItem item() { @property TabItem item() {
return _item; return _item;
} }
@ -334,6 +342,7 @@ class TabControl : WidgetGroupDefaultDrawing {
styleId = _tabStyle; styleId = _tabStyle;
addChild(_moreButton); // first child is always MORE button, the rest corresponds to tab list addChild(_moreButton); // first child is always MORE button, the rest corresponds to tab list
} }
void setStyles(string tabStyle, string tabButtonStyle, string tabButtonTextStyle) { void setStyles(string tabStyle, string tabButtonStyle, string tabButtonTextStyle) {
_tabStyle = tabStyle; _tabStyle = tabStyle;
_tabButtonStyle = tabButtonStyle; _tabButtonStyle = tabButtonStyle;
@ -533,6 +542,7 @@ class TabControl : WidgetGroupDefaultDrawing {
TabItem item = new TabItem(id, label, iconId, tooltipText); TabItem item = new TabItem(id, label, iconId, tooltipText);
return addTab(item, -1, enableCloseButton); return addTab(item, -1, enableCloseButton);
} }
protected MenuItem getMoreButtonPopupMenu() { protected MenuItem getMoreButtonPopupMenu() {
if (moreButtonPopupMenu.assigned) { if (moreButtonPopupMenu.assigned) {
if (auto menu = moreButtonPopupMenu(this)) { if (auto menu = moreButtonPopupMenu(this)) {
@ -553,6 +563,7 @@ class TabControl : WidgetGroupDefaultDrawing {
} }
return null; return null;
} }
/// try to invoke popup menu, return true if popup menu is shown /// try to invoke popup menu, return true if popup menu is shown
protected bool handleMorePopupMenu() { protected bool handleMorePopupMenu() {
if (auto menu = getMoreButtonPopupMenu()) { if (auto menu = getMoreButtonPopupMenu()) {
@ -567,6 +578,7 @@ class TabControl : WidgetGroupDefaultDrawing {
} }
return false; return false;
} }
/// override to handle specific actions /// override to handle specific actions
override bool handleAction(const Action a) { override bool handleAction(const Action a) {
if (a.id == StandardAction.TabSelectItem) { if (a.id == StandardAction.TabSelectItem) {
@ -575,6 +587,7 @@ class TabControl : WidgetGroupDefaultDrawing {
} }
return super.handleAction(a); return super.handleAction(a);
} }
protected bool onMouse(Widget source, MouseEvent event) { protected bool onMouse(Widget source, MouseEvent event) {
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) { if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
if (source.compareId("MORE")) { if (source.compareId("MORE")) {
@ -594,6 +607,7 @@ class TabControl : WidgetGroupDefaultDrawing {
} }
return true; return true;
} }
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout). /// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
override void measure(int parentWidth, int parentHeight) { override void measure(int parentWidth, int parentHeight) {
//Log.d("tabControl.measure enter"); //Log.d("tabControl.measure enter");
@ -627,6 +641,7 @@ class TabControl : WidgetGroupDefaultDrawing {
measuredContent(parentWidth, parentHeight, sz.x, sz.y); measuredContent(parentWidth, parentHeight, sz.x, sz.y);
//Log.d("tabControl.measure exit"); //Log.d("tabControl.measure exit");
} }
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
override void layout(Rect rc) { override void layout(Rect rc) {
//Log.d("tabControl.layout enter"); //Log.d("tabControl.layout enter");
@ -736,7 +751,6 @@ class TabControl : WidgetGroupDefaultDrawing {
if (tabChanged.assigned) if (tabChanged.assigned)
tabChanged(_selectedTabId, previousSelectedTab); tabChanged(_selectedTabId, previousSelectedTab);
} }
} }
/// container for widgets controlled by TabControl /// container for widgets controlled by TabControl
@ -799,6 +813,7 @@ class TabHost : FrameLayout, TabHandler {
requestLayout(); requestLayout();
return this; return this;
} }
/// add new tab by id and label string /// add new tab by id and label string
TabHost addTab(Widget widget, dstring label, string iconId = null, bool enableCloseButton = false, dstring tooltipText = null) { TabHost addTab(Widget widget, dstring label, string iconId = null, bool enableCloseButton = false, dstring tooltipText = null) {
assert(_tabControl !is null, "No TabControl set for TabHost"); assert(_tabControl !is null, "No TabControl set for TabHost");
@ -810,6 +825,7 @@ class TabHost : FrameLayout, TabHandler {
addChild(widget); addChild(widget);
return this; return this;
} }
/// add new tab by id and label string resource id /// 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) { TabHost addTab(Widget widget, string labelResourceId, string iconId = null, bool enableCloseButton = false, dstring tooltipText = null) {
assert(_tabControl !is null, "No TabControl set for TabHost"); assert(_tabControl !is null, "No TabControl set for TabHost");

View File

@ -366,7 +366,7 @@ public:
requestLayout(); requestLayout();
return this; return this;
} }
immutable static int FOCUS_RECT_PADDING = 2; static enum FOCUS_RECT_PADDING = 2;
/// get padding (between background bounds and content of widget) /// get padding (between background bounds and content of widget)
@property Rect padding() const { @property Rect padding() const {
// get max padding from style padding and background drawable padding // get max padding from style padding and background drawable padding
@ -640,16 +640,16 @@ public:
/// returns widget visibility (Visible, Invisible, Gone) /// returns widget visibility (Visible, Invisible, Gone)
@property Visibility visibility() { return _visibility; } @property Visibility visibility() { return _visibility; }
/// sets widget visibility (Visible, Invisible, Gone) /// sets widget visibility (Visible, Invisible, Gone)
@property Widget visibility(Visibility visible) { @property Widget visibility(Visibility newVisibility) {
if (_visibility != visible) { if (_visibility != newVisibility) {
if ((_visibility == Visibility.Gone) || (visible == Visibility.Gone)) { if ((_visibility == Visibility.Gone) || (newVisibility == Visibility.Gone)) {
if (parent) if (parent)
parent.requestLayout(); parent.requestLayout();
else else
requestLayout(); requestLayout();
} else } else
invalidate(); invalidate();
_visibility = visible; _visibility = newVisibility;
} }
return this; return this;
} }
@ -868,9 +868,9 @@ public:
this.rect = widget.pos; this.rect = widget.pos;
} }
static if (BACKEND_GUI) { static if (BACKEND_GUI) {
static immutable int NEAR_THRESHOLD = 10; static enum NEAR_THRESHOLD = 10;
} else { } else {
static immutable int NEAR_THRESHOLD = 1; static enum NEAR_THRESHOLD = 1;
} }
bool nearX(TabOrderInfo v) { bool nearX(TabOrderInfo v) {
return v.rect.left >= rect.left - NEAR_THRESHOLD && v.rect.left <= rect.left + NEAR_THRESHOLD; return v.rect.left >= rect.left - NEAR_THRESHOLD && v.rect.left <= rect.left + NEAR_THRESHOLD;