Skip to content

Commit

Permalink
Add flash cursor command and functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
SpartanJ committed Feb 7, 2025
1 parent 75c6366 commit 0da3ffc
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 29 deletions.
13 changes: 11 additions & 2 deletions include/eepp/ui/uicodeeditor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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 };
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -1104,6 +1111,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
void refreshTag();

bool isNotMonospace() const;

void flashCursor();
};

}} // namespace EE::UI
Expand Down
43 changes: 21 additions & 22 deletions src/eepp/ui/tools/uicodeeditorsplitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() );
} );
Expand Down Expand Up @@ -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() );
} );
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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(
Expand All @@ -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 )
Expand Down Expand Up @@ -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();
Expand Down
47 changes: 45 additions & 2 deletions src/eepp/ui/uicodeeditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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() );
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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" } );
Expand Down
6 changes: 3 additions & 3 deletions src/eepp/ui/uipushbutton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) {
Expand Down
2 changes: 2 additions & 0 deletions src/tools/ecode/appconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );
Expand Down
1 change: 1 addition & 0 deletions src/tools/ecode/appconfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
1 change: 1 addition & 0 deletions src/tools/ecode/ecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) );
Expand Down
13 changes: 13 additions & 0 deletions src/tools/ecode/settingsmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" ) )
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 0da3ffc

Please sign in to comment.