allow to show editor state in status bar panel - close #451; for implementing of buggins/dlangide#282

This commit is contained in:
Vadim Lopatin 2017-09-26 16:37:42 +03:00
parent 2ed9a8d394
commit ecf3f1cc56
4 changed files with 133 additions and 4 deletions

View File

@ -49,6 +49,24 @@ interface EditableContentChangeListener {
void onEditableContentChanged(EditableContent source); 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 /// Flags used for search / replace / text highlight
enum TextSearchFlag { enum TextSearchFlag {
CaseSensitive = 1, CaseSensitive = 1,
@ -311,6 +329,37 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
/// Signal to emit when editor content is changed /// Signal to emit when editor content is changed
Signal!EditableContentChangeListener contentChange; 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 to support modification of client rect after change, e.g. apply offset
override protected void handleClientRectLayout(ref Rect rc) { override protected void handleClientRectLayout(ref Rect rc) {
updateLeftPaneWidth(); updateLeftPaneWidth();
@ -785,6 +834,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
/// sets replace mode flag /// sets replace mode flag
@property EditWidgetBase replaceMode(bool replaceMode) { @property EditWidgetBase replaceMode(bool replaceMode) {
_replaceMode = replaceMode; _replaceMode = replaceMode;
handleEditorStateChange();
invalidate(); invalidate();
return this; return this;
} }
@ -922,6 +972,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
if (contentChange.assigned) { if (contentChange.assigned) {
contentChange(_content); contentChange(_content);
} }
handleEditorStateChange();
return; return;
} }
protected bool _lastReportedModifiedState; protected bool _lastReportedModifiedState;
@ -1115,6 +1166,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
_content.correctPosition(_selectionRange.end); _content.correctPosition(_selectionRange.end);
if (_selectionRange.empty) if (_selectionRange.empty)
_selectionRange = TextRange(_caretPos, _caretPos); _selectionRange = TextRange(_caretPos, _caretPos);
handleEditorStateChange();
} }
@ -1176,6 +1228,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
} }
invalidate(); invalidate();
requestActionsUpdate(); requestActionsUpdate();
handleEditorStateChange();
} }
protected dstring _textToHighlight; protected dstring _textToHighlight;
@ -1205,6 +1258,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
_caretPos = newPos; _caretPos = newPos;
updateSelectionAfterCursorMovement(oldCaretPos, false); updateSelectionAfterCursorMovement(oldCaretPos, false);
} }
handleEditorStateChange();
} }
protected void selectLineByMouse(int x, int y, bool onSameLineOnly = true) { protected void selectLineByMouse(int x, int y, bool onSameLineOnly = true) {
@ -1222,6 +1276,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
_caretPos = newPos; _caretPos = newPos;
updateSelectionAfterCursorMovement(oldCaretPos, false); updateSelectionAfterCursorMovement(oldCaretPos, false);
} }
handleEditorStateChange();
} }
protected void updateCaretPositionByMouse(int x, int y, bool selecting) { protected void updateCaretPositionByMouse(int x, int y, bool selecting) {
@ -1232,6 +1287,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
updateSelectionAfterCursorMovement(oldCaretPos, selecting); updateSelectionAfterCursorMovement(oldCaretPos, selecting);
invalidate(); invalidate();
} }
handleEditorStateChange();
} }
/// generate string of spaces, to reach next tab position /// generate string of spaces, to reach next tab position
@ -1295,6 +1351,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
//_selectionRange.start = _caretPos; //_selectionRange.start = _caretPos;
//_selectionRange.end = _caretPos; //_selectionRange.end = _caretPos;
ensureCaretVisible(); ensureCaretVisible();
handleEditorStateChange();
return true; return true;
} }
@ -1308,6 +1365,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
return; return;
_selectionRange = range; _selectionRange = range;
_caretPos = range.end; _caretPos = range.end;
handleEditorStateChange();
} }
/// override to handle specific actions state (e.g. change enabled state for supported actions) /// override to handle specific actions state (e.g. change enabled state for supported actions)
@ -1948,6 +2006,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
invalidate(); invalidate();
if (makeVisible) if (makeVisible)
ensureCaretVisible(center); ensureCaretVisible(center);
handleEditorStateChange();
} }
} }
@ -2047,6 +2106,7 @@ class EditLine : EditWidgetBase {
invalidate(); invalidate();
} }
updateScrollBars(); updateScrollBars();
handleEditorStateChange();
} }
protected dstring applyPasswordChar(dstring s) { protected dstring applyPasswordChar(dstring s) {
@ -2525,6 +2585,7 @@ class EditBox : EditWidgetBase {
invalidate(); invalidate();
} }
updateScrollBars(); updateScrollBars();
handleEditorStateChange();
} }
override protected Rect textPosToClient(TextPosition p) { override protected Rect textPosToClient(TextPosition p) {
@ -2790,6 +2851,7 @@ class EditBox : EditWidgetBase {
EditOperation op = new EditOperation(EditAction.Replace, r, [""d, ""d]); EditOperation op = new EditOperation(EditAction.Replace, r, [""d, ""d]);
_content.performOperation(op, this); _content.performOperation(op, this);
_caretPos = oldCaretPos; _caretPos = oldCaretPos;
handleEditorStateChange();
} }
return true; return true;
case DeleteLine: case DeleteLine:
@ -3506,13 +3568,16 @@ class FindPanel : HorizontalLayout {
} }
return this; return this;
} }
@property dstring searchText() { @property dstring searchText() {
return _edFind.text; return _edFind.text;
} }
@property FindPanel searchText(dstring newText) { @property FindPanel searchText(dstring newText) {
_edFind.text = newText; _edFind.text = newText;
return this; return this;
} }
this(EditBox editor, bool selectionOnly, bool replace, dstring initialText = ""d) { this(EditBox editor, bool selectionOnly, bool replace, dstring initialText = ""d) {
_replaceMode = replace; _replaceMode = replace;
import dlangui.dml.parser; import dlangui.dml.parser;

View File

@ -22,6 +22,7 @@ module dlangui.widgets.statusline;
import dlangui.widgets.layouts; import dlangui.widgets.layouts;
import dlangui.widgets.controls; import dlangui.widgets.controls;
import dlangui.widgets.editors;
class StatusLinePanelBase : HorizontalLayout { class StatusLinePanelBase : HorizontalLayout {
this(string ID) { 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 /// Status line control
class StatusLine : HorizontalLayout { class StatusLine : HorizontalLayout, EditorStateListener {
protected TextWidget _defStatus; protected TextWidget _defStatus;
protected StatusLineBackgroundOperationPanel _backgroundOperationPanel; protected StatusLineBackgroundOperationPanel _backgroundOperationPanel;
protected StatusLineEditorStatePanel _editorStatePanel;
this() { this() {
super("STATUS_LINE"); super("STATUS_LINE");
styleId = STYLE_STATUS_LINE; styleId = STYLE_STATUS_LINE;
@ -115,7 +158,9 @@ class StatusLine : HorizontalLayout {
_defStatus.text = " "d; _defStatus.text = " "d;
addChild(_defStatus); addChild(_defStatus);
_backgroundOperationPanel = new StatusLineBackgroundOperationPanel("BACKGROUND_OP_STATUS"); _backgroundOperationPanel = new StatusLineBackgroundOperationPanel("BACKGROUND_OP_STATUS");
_editorStatePanel = new StatusLineEditorStatePanel("EDITOR_STATE_PANEL");
addChild(_backgroundOperationPanel); addChild(_backgroundOperationPanel);
addChild(_editorStatePanel);
} }
/// set text to show in status line in specific panel /// set text to show in status line in specific panel
void setStatusText(string itemId, dstring value) { void setStatusText(string itemId, dstring value) {
@ -129,4 +174,14 @@ class StatusLine : HorizontalLayout {
void setBackgroundOperationStatus(string icon, dstring statusText = null) { void setBackgroundOperationStatus(string icon, dstring statusText = null) {
_backgroundOperationPanel.setBackgroundOperationStatus(icon, statusText); _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);
}
} }

View File

@ -1040,8 +1040,17 @@ class TabWidget : VerticalLayout, TabHandler, TabCloseHandler {
return _tabControl._selectedTabId; 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 /// get tab content widget by id
Widget selectedTabBody() { @property Widget selectedTabBody() {
return _tabHost.tabBody(_tabControl._selectedTabId); return _tabHost.tabBody(_tabControl._selectedTabId);
} }

View File

@ -1 +1 @@
v0.9.147 v0.9.148