diff --git a/src/dlangui/core/collections.d b/src/dlangui/core/collections.d index 09f37eaf..5f2f857d 100644 --- a/src/dlangui/core/collections.d +++ b/src/dlangui/core/collections.d @@ -137,8 +137,8 @@ struct Collection(T, bool ownItems = false) { T result = _items[index]; for (size_t i = index; i + 1 < _len; i++) _items[i] = _items[i + 1]; - _items[_len] = T.init; _len--; + _items[_len] = T.init; return result; } /// remove single item by value - if present in collection, returning true if item was found and removed @@ -185,6 +185,13 @@ struct Collection(T, bool ownItems = false) { return remove(0); } + /// peek first item + @property T peekFront() { + if (empty) + return T.init; // no items + return _items[0]; + } + /// insert item at beginning of collection void pushFront(T item) { add(item, 0); @@ -197,6 +204,13 @@ struct Collection(T, bool ownItems = false) { return remove(length - 1); } + /// peek last item + @property T peekBack() { + if (empty) + return T.init; // no items + return _items[length - 1]; + } + /// insert item at end of collection void pushBack(T item) { add(item); diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 1483f265..18ca385c 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -325,6 +325,8 @@ enum EditAction { /// replace whole content ReplaceContent, + /// saved content + SaveContent, } /// edit operation details for EditableContent @@ -451,6 +453,18 @@ class UndoBuffer { void clear() { _undoList.clear(); _redoList.clear(); + _savedState = null; + } + + protected EditOperation _savedState; + + /// current state is saved + void saved() { + _savedState = _undoList.peekBack; + } + /// returns true if content has been changed since last saved() or clear() call + @property bool modified() { + return _savedState !is _undoList.peekBack; } } @@ -459,6 +473,11 @@ interface EditableContentListener { void onContentChange(EditableContent content, EditOperation operation, ref TextRange rangeBefore, ref TextRange rangeAfter, Object source); } +/// Modified state change listener +interface ModifiedStateListener { + void onModifiedStateChange(Widget source, bool modified); +} + alias TokenPropString = ubyte[]; /// interface for custom syntax highlight @@ -476,6 +495,10 @@ class EditableContent { _undoBuffer = new UndoBuffer(); } + @property bool modified() { + return _undoBuffer.modified; + } + protected UndoBuffer _undoBuffer; protected SyntaxHighlighter _syntaxHighlighter; @@ -547,6 +570,14 @@ class EditableContent { handleContentChange(new EditOperation(EditAction.ReplaceContent), rangeBefore, rangeAfter, this); } + /// call listener to say that content is saved + void notifyContentSaved() { + TextRange rangeBefore; + TextRange rangeAfter; + // notify about content change + handleContentChange(new EditOperation(EditAction.SaveContent), rangeBefore, rangeAfter, this); + } + protected void updateTokenProps(int startLine, int endLine) { clearTokenProps(startLine, endLine); if (_syntaxHighlighter) { @@ -926,8 +957,8 @@ class EditableContent { op.newRange = rangeAfter; op.oldContent = oldcontent; replaceRange(rangeBefore, rangeAfter, newcontent); - handleContentChange(op, rangeBefore, rangeAfter, source); _undoBuffer.saveForUndo(op); + handleContentChange(op, rangeBefore, rangeAfter, source); return true; } return false; @@ -1013,6 +1044,7 @@ class EditableContent { } // EOF _format = lines.textFormat; + _undoBuffer.clear(); notifyContentReplaced(); return true; } catch (Exception e) { @@ -1047,7 +1079,8 @@ class EditableContent { for (int i = 0; i < _lines.length; i++) { writer.writeLine(_lines[i]); } - // EOF + _undoBuffer.saved(); + notifyContentSaved(); return true; } catch (Exception e) { Log.e("Exception while trying to write file ", filename, " ", e.toString); @@ -1116,6 +1149,9 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction protected uint _foldingPaneWidth = 16; protected uint _modificationMarksPaneWidth = 8; + /// Modified state change listener + Signal!ModifiedStateListener onModifiedStateChangeListener; + /// override to support modification of client rect after change, e.g. apply offset override protected void handleClientRectLayout(ref Rect rc) { updateLeftPaneWidth(); @@ -1470,6 +1506,8 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction ensureCaretVisible(); correctCaretPos(); requestLayout(); + } else if (operation.action == EditAction.SaveContent) { + // saved } else { _caretPos = rangeAfter.end; _selectionRange.start = _caretPos; @@ -1481,9 +1519,15 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction // TODO: do something better (e.g. take into account ranges when correcting) } invalidate(); + if (onModifiedStateChangeListener.assigned) { + if (_lastReportedModifiedState != content.modified) { + _lastReportedModifiedState = content.modified; + onModifiedStateChangeListener(this, content.modified); + } + } return; } - + protected bool _lastReportedModifiedState; /// get widget text override @property dstring text() { return _content.text; } diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d index 7c6aa730..699c1830 100644 --- a/src/dlangui/widgets/tabs.d +++ b/src/dlangui/widgets/tabs.d @@ -122,7 +122,10 @@ class TabItemWidget : HorizontalLayout { } return true; } - protected void setItem(TabItem item) { + @property TabItem item() { + return _item; + } + @property void setItem(TabItem item) { _item = item; if (item.iconId !is null) { _icon.visibility = Visibility.Visible; @@ -304,6 +307,29 @@ class TabControl : WidgetGroupDefaultDrawing { } return this; } + + /// change name of tab + void renameTab(string ID, dstring name) { + int index = _items.indexById(id); + if (index >= 0) { + renameTab(index, name); + } + } + + /// change name of tab + void renameTab(int index, dstring name) { + _items[index].text = name; + for (int i = 0; i < _children.count; i++) { + TabItemWidget widget = cast (TabItemWidget)_children[i]; + if (widget && widget.item is _items[index]) { + widget.setItem(_items[index]); + requestLayout(); + break; + } + } + } + + /// add new tab TabControl addTab(TabItem item, int index = -1, bool enableCloseButton = false) { _items.insert(item, index); @@ -587,6 +613,16 @@ class TabWidget : VerticalLayout, TabHandler { return this; } + /// change name of tab + void renameTab(string ID, dstring name) { + _tabControl.renameTab(ID, name); + } + + /// change name of tab + void renameTab(int index, dstring name) { + _tabControl.renameTab(index, name); + } + /// select tab void selectTab(string ID, bool updateAccess = true) { _tabHost.selectTab(ID, updateAccess);