From ecf3f1cc569953086d130ec071daeb51641c30ae Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Tue, 26 Sep 2017 16:37:42 +0300 Subject: [PATCH] allow to show editor state in status bar panel - close #451; for implementing of buggins/dlangide#282 --- src/dlangui/widgets/editors.d | 67 +++++++++++++++++++++++++++++++- src/dlangui/widgets/statusline.d | 57 ++++++++++++++++++++++++++- src/dlangui/widgets/tabs.d | 11 +++++- views/DLANGUI_VERSION | 2 +- 4 files changed, 133 insertions(+), 4 deletions(-) diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 009dad0a..bafdf480 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -49,6 +49,24 @@ interface EditableContentChangeListener { void onEditableContentChanged(EditableContent source); } +/// editor state to display in status line +struct EditorStateInfo { + /// editor mode: true if replace mode, false if insert mode + bool replaceMode; + /// cursor position column (1-based) + int col; + /// cursor position line (1-based) + int line; + /// character under cursor + dchar character; + /// returns true if editor is in active state + @property bool active() { return col > 0 && line > 0; } +} + +interface EditorStateListener { + void onEditorStateUpdate(Widget source, ref EditorStateInfo editorState); +} + /// Flags used for search / replace / text highlight enum TextSearchFlag { CaseSensitive = 1, @@ -311,6 +329,37 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction /// Signal to emit when editor content is changed Signal!EditableContentChangeListener contentChange; + /// Signal to emit when editor cursor position or Insert/Replace mode is changed. + Signal!EditorStateListener editorStateChange; + + /// sets focus to this widget or suitable focusable child, returns previously focused widget + override Widget setFocus(FocusReason reason = FocusReason.Unspecified) { + Widget res = super.setFocus(reason); + if (focused) + handleEditorStateChange(); + return res; + } + + /// updates editorStateChange with recent position + protected void handleEditorStateChange() { + if (!editorStateChange.assigned) + return; + EditorStateInfo info; + if (visible) { + info.replaceMode = _replaceMode; + info.line = _caretPos.line + 1; + info.col = _caretPos.pos + 1; + if (_caretPos.line >= 0 && _caretPos.line < _content.length) { + dstring line = _content.line(_caretPos.line); + if (_caretPos.pos >= 0 && _caretPos.pos < line.length) + info.character = line[_caretPos.pos]; + else + info.character = '\n'; + } + } + editorStateChange(this, info); + } + /// override to support modification of client rect after change, e.g. apply offset override protected void handleClientRectLayout(ref Rect rc) { updateLeftPaneWidth(); @@ -785,6 +834,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction /// sets replace mode flag @property EditWidgetBase replaceMode(bool replaceMode) { _replaceMode = replaceMode; + handleEditorStateChange(); invalidate(); return this; } @@ -922,6 +972,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction if (contentChange.assigned) { contentChange(_content); } + handleEditorStateChange(); return; } protected bool _lastReportedModifiedState; @@ -1115,6 +1166,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction _content.correctPosition(_selectionRange.end); if (_selectionRange.empty) _selectionRange = TextRange(_caretPos, _caretPos); + handleEditorStateChange(); } @@ -1176,6 +1228,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction } invalidate(); requestActionsUpdate(); + handleEditorStateChange(); } protected dstring _textToHighlight; @@ -1205,6 +1258,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction _caretPos = newPos; updateSelectionAfterCursorMovement(oldCaretPos, false); } + handleEditorStateChange(); } protected void selectLineByMouse(int x, int y, bool onSameLineOnly = true) { @@ -1222,6 +1276,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction _caretPos = newPos; updateSelectionAfterCursorMovement(oldCaretPos, false); } + handleEditorStateChange(); } protected void updateCaretPositionByMouse(int x, int y, bool selecting) { @@ -1232,6 +1287,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction updateSelectionAfterCursorMovement(oldCaretPos, selecting); invalidate(); } + handleEditorStateChange(); } /// generate string of spaces, to reach next tab position @@ -1295,6 +1351,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction //_selectionRange.start = _caretPos; //_selectionRange.end = _caretPos; ensureCaretVisible(); + handleEditorStateChange(); return true; } @@ -1308,6 +1365,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction return; _selectionRange = range; _caretPos = range.end; + handleEditorStateChange(); } /// override to handle specific actions state (e.g. change enabled state for supported actions) @@ -1948,6 +2006,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction invalidate(); if (makeVisible) ensureCaretVisible(center); + handleEditorStateChange(); } } @@ -2047,6 +2106,7 @@ class EditLine : EditWidgetBase { invalidate(); } updateScrollBars(); + handleEditorStateChange(); } protected dstring applyPasswordChar(dstring s) { @@ -2525,6 +2585,7 @@ class EditBox : EditWidgetBase { invalidate(); } updateScrollBars(); + handleEditorStateChange(); } override protected Rect textPosToClient(TextPosition p) { @@ -2790,6 +2851,7 @@ class EditBox : EditWidgetBase { EditOperation op = new EditOperation(EditAction.Replace, r, [""d, ""d]); _content.performOperation(op, this); _caretPos = oldCaretPos; + handleEditorStateChange(); } return true; case DeleteLine: @@ -3499,20 +3561,23 @@ class FindPanel : HorizontalLayout { protected bool _replaceMode; /// returns true if panel is working in replace mode @property bool replaceMode() { return _replaceMode; } - @property FindPanel replaceMode(bool newMode) { + @property FindPanel replaceMode(bool newMode) { if (newMode != _replaceMode) { _replaceMode = newMode; childById("replace").visibility = newMode ? Visibility.Visible : Visibility.Gone; } return this; } + @property dstring searchText() { return _edFind.text; } + @property FindPanel searchText(dstring newText) { _edFind.text = newText; return this; } + this(EditBox editor, bool selectionOnly, bool replace, dstring initialText = ""d) { _replaceMode = replace; import dlangui.dml.parser; diff --git a/src/dlangui/widgets/statusline.d b/src/dlangui/widgets/statusline.d index 6517c51a..da4097da 100644 --- a/src/dlangui/widgets/statusline.d +++ b/src/dlangui/widgets/statusline.d @@ -22,6 +22,7 @@ module dlangui.widgets.statusline; import dlangui.widgets.layouts; import dlangui.widgets.controls; +import dlangui.widgets.editors; class StatusLinePanelBase : HorizontalLayout { this(string ID) { @@ -100,10 +101,52 @@ class StatusLineBackgroundOperationPanel : StatusLineTextAndIconPanel { } } +class StatusLineEditorStatePanel : StatusLineTextPanel { + EditorStateInfo _editorState; + + this(string ID = "statusLineEditorStateLabel") { + super(ID); + _text.alignment = Align.VCenter | Align.Right; + //_text.backgroundColor = 0x80FF0000; + //backgroundColor = 0x8000FF00; + updateSize(); + visibility = Visibility.Gone; + } + + dstring makeStateString() { + if (!_editorState.active) + return null; + import std.string : format; + return "%d : %d ch=0x%05x %s "d.format(_editorState.line, _editorState.col, _editorState.character, _editorState.replaceMode ? "OVR"d : "INS"d); + } + + private void updateSize() { + FontRef fnt = font; + Point sz = fnt.textSize(" ch=0x00000 000000 : 000 INS "d); + _text.minWidth = sz.x; + } + + /// handle theme change: e.g. reload some themed resources + override void onThemeChanged() { + updateSize(); + } + + void setState(Widget source, ref EditorStateInfo editorState) { + if (editorState != _editorState) { + _editorState = editorState; + text = makeStateString(); + Visibility newVisibility = _editorState.active ? Visibility.Visible : Visibility.Gone; + if (newVisibility != visibility) + visibility = newVisibility; + } + } +} + /// Status line control -class StatusLine : HorizontalLayout { +class StatusLine : HorizontalLayout, EditorStateListener { protected TextWidget _defStatus; protected StatusLineBackgroundOperationPanel _backgroundOperationPanel; + protected StatusLineEditorStatePanel _editorStatePanel; this() { super("STATUS_LINE"); styleId = STYLE_STATUS_LINE; @@ -115,7 +158,9 @@ class StatusLine : HorizontalLayout { _defStatus.text = " "d; addChild(_defStatus); _backgroundOperationPanel = new StatusLineBackgroundOperationPanel("BACKGROUND_OP_STATUS"); + _editorStatePanel = new StatusLineEditorStatePanel("EDITOR_STATE_PANEL"); addChild(_backgroundOperationPanel); + addChild(_editorStatePanel); } /// set text to show in status line in specific panel void setStatusText(string itemId, dstring value) { @@ -129,4 +174,14 @@ class StatusLine : HorizontalLayout { void setBackgroundOperationStatus(string icon, dstring statusText = null) { _backgroundOperationPanel.setBackgroundOperationStatus(icon, statusText); } + + /// EditorStateListener implementation + override void onEditorStateUpdate(Widget source, ref EditorStateInfo editorState) { + _editorStatePanel.setState(source, editorState); + } + + void hideEditorState() { + EditorStateInfo editorState; + _editorStatePanel.setState(null, editorState); + } } diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d index 56639ec6..4ff73e09 100644 --- a/src/dlangui/widgets/tabs.d +++ b/src/dlangui/widgets/tabs.d @@ -1040,8 +1040,17 @@ class TabWidget : VerticalLayout, TabHandler, TabCloseHandler { return _tabControl._selectedTabId; } + /// focus selected tab body + void focusSelectedTab() { + if (!visible) + return; + Widget w = selectedTabBody; + if (w) + w.setFocus(); + } + /// get tab content widget by id - Widget selectedTabBody() { + @property Widget selectedTabBody() { return _tabHost.tabBody(_tabControl._selectedTabId); } diff --git a/views/DLANGUI_VERSION b/views/DLANGUI_VERSION index c523cb39..15fedade 100644 --- a/views/DLANGUI_VERSION +++ b/views/DLANGUI_VERSION @@ -1 +1 @@ -v0.9.147 \ No newline at end of file +v0.9.148 \ No newline at end of file