mirror of https://github.com/buggins/dlangui.git
allow to show editor state in status bar panel - close #451; for implementing of buggins/dlangide#282
This commit is contained in:
parent
2ed9a8d394
commit
ecf3f1cc56
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
v0.9.147
|
v0.9.148
|
Loading…
Reference in New Issue