mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-27 16:38:20 +03:00
Add flash cursor command and functionality.
This commit is contained in:
@@ -779,6 +779,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
|
||||
Float getPluginsGutterSpace() const;
|
||||
|
||||
void setEnableFlashCursor( bool enable ) { mEnableFlashCursor = enable; }
|
||||
|
||||
bool isEnabledFlashCursor() { return mEnableFlashCursor; }
|
||||
|
||||
protected:
|
||||
struct LastXOffset {
|
||||
TextPosition position{ 0, 0 };
|
||||
@@ -826,10 +830,11 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
bool mFoldsAlwaysVisible{ false };
|
||||
bool mFoldsVisible{ false };
|
||||
bool mFoldsIsFirst{ true };
|
||||
bool mEnableFlashCursor{ false };
|
||||
Uint32 mTabWidth;
|
||||
std::atomic<size_t> mHighlightWordProcessing{ false };
|
||||
TextRange mLinkPosition;
|
||||
String mLink;
|
||||
Uint32 mTabWidth;
|
||||
Vector2f mScroll;
|
||||
Float mMouseWheelScroll;
|
||||
Float mFontSize;
|
||||
@@ -854,6 +859,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
Color mMinimapHoverColor;
|
||||
Color mMinimapSelectionColor;
|
||||
Color mMinimapHighlightColor;
|
||||
Color mPreviewColor;
|
||||
SyntaxColorScheme mColorScheme;
|
||||
UIScrollBar* mVScrollBar;
|
||||
UIScrollBar* mHScrollBar;
|
||||
@@ -872,7 +878,6 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
TextRanges mHighlightWordCache;
|
||||
Mutex mHighlightWordCacheMutex;
|
||||
TextRange mHighlightTextRange;
|
||||
Color mPreviewColor;
|
||||
TextRange mPreviewColorRange;
|
||||
std::vector<UICodeEditorPlugin*> mPlugins;
|
||||
UILoader* mLoader{ nullptr };
|
||||
@@ -903,6 +908,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
String::HashType mTagFoldRange{ 0 };
|
||||
Uint32 mTabIndentCharacter{ 187 /*'»'*/ };
|
||||
CharacterAlignment mTabIndentAlignment{ CharacterAlignment::Center };
|
||||
Uint32 mModDownCount{ 0 };
|
||||
Clock mModDownClock;
|
||||
|
||||
UICodeEditor( const std::string& elementTag, const bool& autoRegisterBaseCommands = true,
|
||||
const bool& autoRegisterBaseKeybindings = true );
|
||||
@@ -1104,6 +1111,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
void refreshTag();
|
||||
|
||||
bool isNotMonospace() const;
|
||||
|
||||
void flashCursor();
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -244,24 +244,24 @@ UICodeEditor* UICodeEditorSplitter::createCodeEditor() {
|
||||
registerSplitterCommands( doc );
|
||||
/* Splitter commands */
|
||||
|
||||
editor->addEventListener( Event::OnFocus, [this]( const Event* event ) {
|
||||
editor->on( Event::OnFocus, [this]( const Event* event ) {
|
||||
setCurrentWidget( event->getNode()->asType<UICodeEditor>() );
|
||||
} );
|
||||
editor->addEventListener( Event::OnTextChanged, [this]( const Event* event ) {
|
||||
editor->on( Event::OnTextChanged, [this]( const Event* event ) {
|
||||
mClient->onDocumentModified( event->getNode()->asType<UICodeEditor>(),
|
||||
event->getNode()->asType<UICodeEditor>()->getDocument() );
|
||||
} );
|
||||
editor->addEventListener( Event::OnSelectionChanged, [this]( const Event* event ) {
|
||||
editor->on( Event::OnSelectionChanged, [this]( const Event* event ) {
|
||||
mClient->onDocumentSelectionChange(
|
||||
event->getNode()->asType<UICodeEditor>(),
|
||||
event->getNode()->asType<UICodeEditor>()->getDocument() );
|
||||
} );
|
||||
editor->addEventListener( Event::OnCursorPosChange, [this]( const Event* event ) {
|
||||
editor->on( Event::OnCursorPosChange, [this]( const Event* event ) {
|
||||
mClient->onDocumentCursorPosChange(
|
||||
event->getNode()->asType<UICodeEditor>(),
|
||||
event->getNode()->asType<UICodeEditor>()->getDocument() );
|
||||
} );
|
||||
editor->addEventListener( Event::OnDocumentUndoRedo, [this]( const Event* event ) {
|
||||
editor->on( Event::OnDocumentUndoRedo, [this]( const Event* event ) {
|
||||
mClient->onDocumentUndoRedo( event->getNode()->asType<UICodeEditor>(),
|
||||
event->getNode()->asType<UICodeEditor>()->getDocument() );
|
||||
} );
|
||||
@@ -526,7 +526,7 @@ UICodeEditorSplitter::createCodeEditorInTabWidget( UITabWidget* tabWidget ) {
|
||||
return std::make_pair( (UITab*)nullptr, (UICodeEditor*)nullptr );
|
||||
UICodeEditor* editor = createCodeEditor();
|
||||
mAboutToAddEditor = editor;
|
||||
editor->addEventListener( Event::OnDocumentChanged, [this]( const Event* event ) {
|
||||
editor->on( Event::OnDocumentChanged, [this]( const Event* event ) {
|
||||
mClient->onDocumentStateChanged( event->getNode()->asType<UICodeEditor>(),
|
||||
event->getNode()->asType<UICodeEditor>()->getDocument() );
|
||||
} );
|
||||
@@ -568,10 +568,10 @@ UICodeEditorSplitter::createWidgetInTabWidget( UITabWidget* tabWidget, UIWidget*
|
||||
return std::make_pair( (UITab*)nullptr, (UIWidget*)nullptr );
|
||||
UITab* tab = tabWidget->add( tabName, widget );
|
||||
widget->setData( (UintPtr)tab );
|
||||
widget->addEventListener( Event::OnFocus, [this]( const Event* event ) {
|
||||
widget->on( Event::OnFocus, [this]( const Event* event ) {
|
||||
setCurrentWidget( event->getNode()->asType<UIWidget>() );
|
||||
} );
|
||||
widget->addEventListener( Event::OnTitleChange, [this]( const Event* event ) {
|
||||
widget->on( Event::OnTitleChange, [this]( const Event* event ) {
|
||||
const TextEvent* tevent = static_cast<const TextEvent*>( event );
|
||||
UIWidget* widget = event->getNode()->asType<UIWidget>();
|
||||
UITabWidget* tabWidget = tabWidgetFromWidget( widget );
|
||||
@@ -644,7 +644,7 @@ UITabWidget* UICodeEditorSplitter::createTabWidget( Node* parent ) {
|
||||
},
|
||||
mVisualSplitEdgePercent );
|
||||
}
|
||||
tabWidget->addEventListener( Event::OnTabSelected, [this]( const Event* event ) {
|
||||
tabWidget->on( Event::OnTabSelected, [this]( const Event* event ) {
|
||||
UITabWidget* tabWidget = event->getNode()->asType<UITabWidget>();
|
||||
if ( tabWidget->getTabSelected()->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) {
|
||||
setCurrentEditor(
|
||||
@@ -661,7 +661,7 @@ UITabWidget* UICodeEditorSplitter::createTabWidget( Node* parent ) {
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
tabWidget->addEventListener( Event::OnTabClosed, [this]( const Event* event ) {
|
||||
tabWidget->on( Event::OnTabClosed, [this]( const Event* event ) {
|
||||
onTabClosed( static_cast<const TabEvent*>( event ) );
|
||||
} );
|
||||
if ( mOnTabWidgetCreateCb )
|
||||
@@ -1007,18 +1007,17 @@ bool UICodeEditorSplitter::tryTabClose( UIWidget* widget,
|
||||
"\"%s\" will be lost." )
|
||||
.toUtf8(),
|
||||
editor->getDocument().getFilename() ) );
|
||||
mTryCloseMsgBox->addEventListener( Event::OnConfirm,
|
||||
[this, editor, focusTabBehavior]( const Event* ) {
|
||||
closeTab( editor, focusTabBehavior );
|
||||
} );
|
||||
mTryCloseMsgBox->addEventListener( Event::OnClose,
|
||||
[this, onMsgBoxCloseCb]( const Event* ) {
|
||||
mTryCloseMsgBox = nullptr;
|
||||
if ( mCurEditor )
|
||||
mCurEditor->setFocus();
|
||||
if ( onMsgBoxCloseCb )
|
||||
onMsgBoxCloseCb();
|
||||
} );
|
||||
mTryCloseMsgBox->on( Event::OnConfirm,
|
||||
[this, editor, focusTabBehavior]( const Event* ) {
|
||||
closeTab( editor, focusTabBehavior );
|
||||
} );
|
||||
mTryCloseMsgBox->on( Event::OnClose, [this, onMsgBoxCloseCb]( const Event* ) {
|
||||
mTryCloseMsgBox = nullptr;
|
||||
if ( mCurEditor )
|
||||
mCurEditor->setFocus();
|
||||
if ( onMsgBoxCloseCb )
|
||||
onMsgBoxCloseCb();
|
||||
} );
|
||||
mTryCloseMsgBox->setTitle(
|
||||
widget->getUISceneNode()->i18n( "ask_close_tab", "Close Tab?" ) );
|
||||
mTryCloseMsgBox->center();
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <eepp/graphics/globalbatchrenderer.hpp>
|
||||
#include <eepp/graphics/primitives.hpp>
|
||||
#include <eepp/graphics/renderer/renderer.hpp>
|
||||
#include <eepp/scene/actions/close.hpp>
|
||||
#include <eepp/scene/actions/scale.hpp>
|
||||
#include <eepp/scene/actions/sequence.hpp>
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/system/luapattern.hpp>
|
||||
#include <eepp/system/scopedop.hpp>
|
||||
@@ -128,9 +131,9 @@ UICodeEditor::UICodeEditor( const std::string& elementTag, const bool& autoRegis
|
||||
mLineNumberPaddingLeft( PixelDensity::dpToPx( 6 ) ),
|
||||
mLineNumberPaddingRight( PixelDensity::dpToPx( 6 ) ),
|
||||
mFoldRegionWidth( PixelDensity::dpToPx( 12 ) ),
|
||||
mPreviewColor( Color::Transparent ),
|
||||
mKeyBindings( getUISceneNode()->getWindow()->getInput() ),
|
||||
mFindLongestLineWidthUpdateFrequency( Seconds( 1 ) ),
|
||||
mPreviewColor( Color::Transparent ) {
|
||||
mFindLongestLineWidthUpdateFrequency( Seconds( 1 ) ) {
|
||||
mFlags |= UI_TAB_STOP | UI_OWNS_CHILDS_POSITION | UI_SCROLLABLE;
|
||||
setTextSelection( true );
|
||||
setColorScheme( SyntaxColorScheme::getDefault() );
|
||||
@@ -1107,6 +1110,28 @@ Uint32 UICodeEditor::onTextEditing( const TextEditingEvent& event ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void UICodeEditor::flashCursor() {
|
||||
Vector2f screenStart( getScreenStart() );
|
||||
Vector2f start( screenStart.x + getGutterWidth(), screenStart.y + getPluginsTopSpace() );
|
||||
Vector2f startScroll( start - mScroll );
|
||||
auto offset = getTextPositionOffset( mDoc->getSelection().start(), getLineHeight() );
|
||||
Vector2f cursorPos( startScroll.x + offset.x - getFontHeight() * 0.5f,
|
||||
startScroll.y + offset.y );
|
||||
UIWidget* widget = UIWidget::New();
|
||||
widget->setBorderColor( Color( mCaretColor ).blendAlpha( 100 ).blendAlpha( mAlpha ) );
|
||||
widget->setPixelsPosition( cursorPos.floor() );
|
||||
widget->setBorderWidth( PixelDensity::dpToPx( 2 ) );
|
||||
widget->setPixelsSize( Sizef( getFontHeight(), getFontHeight() ) );
|
||||
widget->setEnabled( false );
|
||||
|
||||
Float scale = eemax( getUISceneNode()->getPixelsSize().getWidth() / getFontHeight(),
|
||||
getUISceneNode()->getPixelsSize().getHeight() / getFontHeight() );
|
||||
|
||||
widget->runAction( Actions::Sequence::New(
|
||||
Actions::Scale::New( { scale, scale }, { 1, 1 }, Milliseconds( 250 ), Ease::Linear ),
|
||||
Actions::Close::New() ) );
|
||||
}
|
||||
|
||||
Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
|
||||
if ( getUISceneNode()->getWindow()->getIME().isEditing() )
|
||||
return 0;
|
||||
@@ -1129,6 +1154,23 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isEnabledFlashCursor() && event.getSanitizedMod() == KeyMod::getDefaultModifier() ) {
|
||||
if ( mModDownCount == 0 )
|
||||
mModDownClock.restart();
|
||||
|
||||
if ( mModDownClock.getElapsedTime() < Milliseconds( 250 ) ) {
|
||||
mModDownCount++;
|
||||
if ( mModDownCount == 5 ) {
|
||||
mModDownCount = 0;
|
||||
flashCursor();
|
||||
}
|
||||
} else
|
||||
mModDownCount = 0;
|
||||
|
||||
mModDownClock.restart();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4221,6 +4263,7 @@ void UICodeEditor::registerCommands() {
|
||||
mDoc->setCommand( "copy-file-path-and-position", [this] { copyFilePath( true ); } );
|
||||
mDoc->setCommand( "find-replace", [this] { showFindReplace(); } );
|
||||
mDoc->setCommand( "open-context-menu", [this] { createContextMenu(); } );
|
||||
mDoc->setCommand( "flash-cursor", [this] { flashCursor(); } );
|
||||
mUnlockedCmd.insert( { "copy", "select-all", "open-containing-folder",
|
||||
"copy-containing-folder-path", "copy-file-path",
|
||||
"copy-file-path-and-position", "open-context-menu", "find-replace" } );
|
||||
|
||||
@@ -449,15 +449,15 @@ void UIPushButton::onAlignChange() {
|
||||
mTextBox->setVerticalAlign( getVerticalAlign() );
|
||||
}
|
||||
|
||||
Uint32 UIPushButton::onKeyDown( const KeyEvent& Event ) {
|
||||
if ( Event.getKeyCode() == KEY_RETURN ) {
|
||||
Uint32 UIPushButton::onKeyDown( const KeyEvent& event ) {
|
||||
if ( event.getKeyCode() == KEY_RETURN || event.getKeyCode() == Window::KEY_KP_ENTER ) {
|
||||
NodeMessage Msg( this, NodeMessage::MouseClick, EE_BUTTON_LMASK );
|
||||
messagePost( &Msg );
|
||||
onMouseClick( Vector2i( 0, 0 ), EE_BUTTON_LMASK );
|
||||
pushState( UIState::StatePressed );
|
||||
}
|
||||
|
||||
return UIWidget::onKeyDown( Event );
|
||||
return UIWidget::onKeyDown( event );
|
||||
}
|
||||
|
||||
Uint32 UIPushButton::onKeyUp( const KeyEvent& Event ) {
|
||||
|
||||
@@ -182,6 +182,7 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath,
|
||||
editor.tabIndentAlignment = characterAlignmentFromString(
|
||||
ini.getValue( "editor", "tab_indent_alignment",
|
||||
characterAlignmentToString( CharacterAlignment::Center ) ) );
|
||||
editor.flashCursor = ini.getValueB( "editor", "flash_cursor", true );
|
||||
|
||||
searchBarConfig.caseSensitive = ini.getValueB( "search_bar", "case_sensitive", false );
|
||||
searchBarConfig.luaPattern = ini.getValueB( "search_bar", "lua_pattern", false );
|
||||
@@ -328,6 +329,7 @@ void AppConfig::save( const std::vector<std::string>& recentFiles,
|
||||
ini.setValue( "editor", "tab_indent_character", editor.tabIndentCharacter );
|
||||
ini.setValue( "editor", "tab_indent_alignment",
|
||||
characterAlignmentToString( editor.tabIndentAlignment ) );
|
||||
ini.setValueB( "editor", "flash_cursor", editor.flashCursor );
|
||||
|
||||
ini.setValueB( "search_bar", "case_sensitive", searchBarConfig.caseSensitive );
|
||||
ini.setValueB( "search_bar", "lua_pattern", searchBarConfig.luaPattern );
|
||||
|
||||
@@ -82,6 +82,7 @@ struct CodeEditorConfig {
|
||||
bool autoReloadOnDiskChange{ false };
|
||||
bool codeFoldingEnabled{ true };
|
||||
bool codeFoldingAlwaysVisible{ false };
|
||||
bool flashCursor{ false };
|
||||
LineWrapMode wrapMode{ LineWrapMode::NoWrap };
|
||||
LineWrapType wrapType{ LineWrapType::Viewport };
|
||||
bool wrapKeepIndentation{ true };
|
||||
|
||||
@@ -2361,6 +2361,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) {
|
||||
editor->setLineWrapType( config.wrapType );
|
||||
editor->setFoldDrawable( findIcon( "chevron-down", PixelDensity::dpToPxI( 12 ) ) );
|
||||
editor->setFoldedDrawable( findIcon( "chevron-right", PixelDensity::dpToPxI( 12 ) ) );
|
||||
editor->setEnableFlashCursor( config.flashCursor );
|
||||
|
||||
doc.setAutoCloseBrackets( !mConfig.editor.autoCloseBrackets.empty() );
|
||||
doc.setAutoCloseBracketsPairs( makeAutoClosePairs( mConfig.editor.autoCloseBrackets ) );
|
||||
|
||||
@@ -658,6 +658,17 @@ UIMenu* SettingsMenu::createDocumentMenu() {
|
||||
"before exiting the program." ) )
|
||||
->setId( "session_snapshot" );
|
||||
|
||||
mGlobalMenu
|
||||
->addCheckBox( i18n( "allow_flash_cursor", "Allow Flashing Cursor" ),
|
||||
mApp->getConfig().editor.flashCursor )
|
||||
->setTooltipText( i18n(
|
||||
"allow_flash_cursor_desc",
|
||||
"When enabled, pressing the default modifier key 5 times within 1.5 seconds will\n"
|
||||
"trigger a visual effect that highlights the current cursor position. A large,\n"
|
||||
"transparent rectangle will briefly animate, shrinking down to the cursor, making it\n"
|
||||
"easier to locate when it's hard to see." ) )
|
||||
->setId( "allow_flash_cursor" );
|
||||
|
||||
mGlobalMenu->addSeparator();
|
||||
|
||||
mGlobalMenu->add( i18n( "line_breaking_column", "Line Breaking Column" ) )
|
||||
@@ -716,6 +727,8 @@ UIMenu* SettingsMenu::createDocumentMenu() {
|
||||
mApp->getConfig().editor.autoReloadOnDiskChange = item->isActive();
|
||||
} else if ( "session_snapshot" == id ) {
|
||||
mApp->getConfig().workspace.sessionSnapshot = item->isActive();
|
||||
} else if ( "allow_flash_cursor" == id ) {
|
||||
mApp->getConfig().editor.flashCursor = item->isActive();
|
||||
}
|
||||
} else if ( "line_breaking_column" == id ) {
|
||||
mApp->getSettingsActions()->setLineBreakingColumn();
|
||||
|
||||
Reference in New Issue
Block a user