diff --git a/include/eepp/math/math.hpp b/include/eepp/math/math.hpp index 59967d6ba..71b151cb4 100644 --- a/include/eepp/math/math.hpp +++ b/include/eepp/math/math.hpp @@ -91,12 +91,7 @@ template inline T roundDown( T x ) { /** @return The number of digits in a number. */ template static T countDigits( T num ) { - T count = 0; - while ( num != 0 ) { - count++; - num /= 10; - } - return count; + return num == 0 ? 1 : (T)log10( std::abs( num ) ) + 1; } }} // namespace EE::Math diff --git a/include/eepp/ui/linewrapping.hpp b/include/eepp/ui/linewrapping.hpp index 6de801a86..8cac50a78 100644 --- a/include/eepp/ui/linewrapping.hpp +++ b/include/eepp/ui/linewrapping.hpp @@ -13,8 +13,18 @@ namespace EE { namespace UI { enum class LineWrapMode { NoWrap, Letter, Word }; +enum class LineWrapType { Viewport, LineBreakingColumn }; + class EE_API LineWrapping { public: + static LineWrapMode toLineWrapMode( std::string mode ); + + static std::string fromLineWrapMode( LineWrapMode mode ); + + static LineWrapType toLineWrapType( std::string type ); + + static std::string fromLineWrapType( LineWrapType type ); + struct Config { LineWrapMode mode{ LineWrapMode::NoWrap }; bool keepIndentation{ true }; @@ -68,7 +78,7 @@ class EE_API LineWrapping { void setConfig( Config config ); - void setMaxWidth( Float maxWidth ); + void setMaxWidth( Float maxWidth, bool forceReconstructBreaks = false ); void setFontStyle( FontStyleConfig fontStyle ); @@ -88,6 +98,9 @@ class EE_API LineWrapping { TextRange getVisualLineRange( Int64 visualLine ) const; + std::shared_ptr getDocument() const; + void setDocument( const std::shared_ptr& doc ); + protected: std::shared_ptr mDoc; FontStyleConfig mFontStyle; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 586c3fef5..89b4412dc 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -653,6 +653,20 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void invalidateLongestLineWidth(); + const LineWrapping& lineWrapping() const { return mLineWrapping; } + + LineWrapMode getLineWrapMode() const { return mLineWrapping.getConfig().mode; } + + void setLineWrapMode( LineWrapMode mode ); + + LineWrapType getLineWrapType() const; + + void setLineWrapType( LineWrapType lineWrapType ); + + bool getLineWrapKeepIndentation() const { return mLineWrapping.getConfig().keepIndentation; } + + void setLineWrapKeepIndentation( bool keep ); + protected: struct LastXOffset { TextPosition position{ 0, 0 }; @@ -692,6 +706,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool mShowIndentationGuides{ false }; bool mShowLinesRelativePosition{ false }; bool mDisplayLockedIcon{ false }; + bool mInvalidateOnLoaded{ false }; std::atomic mHighlightWordProcessing{ false }; TextRange mLinkPosition; String mLink; @@ -767,6 +782,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { size_t mJumpLinesLength{ 5 }; UIIcon* mFileLockIcon{ nullptr }; std::string mFileLockIconName{ "file-lock-fill" }; + LineWrapType mLineWrapType{ LineWrapType::Viewport }; UICodeEditor( const std::string& elementTag, const bool& autoRegisterBaseCommands = true, const bool& autoRegisterBaseKeybindings = true ); diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index dafc29bbb..e82b4289a 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -1299,8 +1299,8 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range, } if ( linesRemoved > 0 ) { - mHighlighter->moveHighlight( range.start().line(), -linesRemoved ); - notifiyDocumenLineMove( range.start().line(), -linesRemoved ); + mHighlighter->moveHighlight( range.end().line(), -linesRemoved ); + notifiyDocumenLineMove( range.end().line(), -linesRemoved ); } notifyTextChanged( { originalRange, "" } ); diff --git a/src/eepp/ui/linewrapping.cpp b/src/eepp/ui/linewrapping.cpp index 3619efa74..9973d88a4 100644 --- a/src/eepp/ui/linewrapping.cpp +++ b/src/eepp/ui/linewrapping.cpp @@ -5,6 +5,44 @@ namespace EE { namespace UI { +LineWrapMode LineWrapping::toLineWrapMode( std::string mode ) { + String::toLowerInPlace( mode ); + if ( mode == "word" ) + return LineWrapMode::Word; + if ( mode == "letter" ) + return LineWrapMode::Letter; + return LineWrapMode::NoWrap; +} + +std::string LineWrapping::fromLineWrapMode( LineWrapMode mode ) { + switch ( mode ) { + case LineWrapMode::Letter: + return "letter"; + case LineWrapMode::Word: + return "word"; + case LineWrapMode::NoWrap: + default: + return "nowrap"; + } +} + +LineWrapType LineWrapping::toLineWrapType( std::string type ) { + String::toLowerInPlace( type ); + if ( "line_breaking_column" == type ) + return LineWrapType::LineBreakingColumn; + return LineWrapType::Viewport; +} + +std::string LineWrapping::fromLineWrapType( LineWrapType type ) { + switch ( type ) { + case LineWrapType::LineBreakingColumn: + return "line_breaking_column"; + case LineWrapType::Viewport: + default: + return "viewport"; + } +} + LineWrapping::LineWrapInfo LineWrapping::computeLineBreaks( const String& string, const FontStyleConfig& fontStyle, Float maxWidth, LineWrapMode mode, @@ -68,7 +106,7 @@ LineWrapping::LineWrapInfo LineWrapping::computeLineBreaks( const String& string xoffset = w + info.paddingStart; } lastSpace = 0; - } else if ( curChar == ' ' ) { + } else if ( curChar == ' ' || curChar == '.' ) { lastSpace = idx; lastWidth = xoffset; } @@ -101,6 +139,7 @@ LineWrapping::LineWrapInfo LineWrapping::computeLineBreaks( const TextDocument& bool bold = ( fontStyle.Style & Text::Style::Bold ) != 0; bool italic = ( fontStyle.Style & Text::Style::Italic ) != 0; Float outlineThickness = fontStyle.OutlineThickness; + bool isMonospace = fontStyle.Font->isMonospace(); size_t lastSpace = 0; Uint32 prevChar = 0; @@ -120,47 +159,41 @@ LineWrapping::LineWrapInfo LineWrapping::computeLineBreaks( const TextDocument& fontStyle, tabWidth ); } - Float w = Text::getTextWidth( text, fontStyle, tabWidth ); + for ( const auto& curChar : text ) { + Float w = !isMonospace ? fontStyle.Font + ->getGlyph( curChar, fontStyle.CharacterSize, bold, italic, + outlineThickness ) + .advance + : hspace; - if ( xoffset + w > maxWidth ) { - for ( const auto& curChar : text ) { - Float w = fontStyle.Font - ->getGlyph( curChar, fontStyle.CharacterSize, bold, italic, - outlineThickness ) - .advance; + if ( curChar == '\t' ) + w += hspace * tabWidth; + else if ( ( curChar ) == '\r' ) + w = 0; - if ( curChar == '\t' ) - w += hspace * tabWidth; - else if ( ( curChar ) == '\r' ) - w = 0; - - if ( curChar != '\r' ) { - w += fontStyle.Font->getKerning( prevChar, curChar, fontStyle.CharacterSize, - bold, italic, outlineThickness ); - prevChar = curChar; - } - - xoffset += w; - - if ( xoffset > maxWidth ) { - if ( mode == LineWrapMode::Word && lastSpace ) { - info.wraps.push_back( lastSpace + 1 ); - xoffset = w + info.paddingStart + ( xoffset - lastWidth ); - } else { - info.wraps.push_back( idx ); - xoffset = w + info.paddingStart; - } - lastSpace = 0; - } else if ( curChar == ' ' ) { - lastSpace = idx; - lastWidth = xoffset; - } - - idx++; + if ( !isMonospace && curChar != '\r' ) { + w += fontStyle.Font->getKerning( prevChar, curChar, fontStyle.CharacterSize, bold, + italic, outlineThickness ); + prevChar = curChar; } - } else { + xoffset += w; - idx += text.size(); + + if ( xoffset > maxWidth ) { + if ( mode == LineWrapMode::Word && lastSpace ) { + info.wraps.push_back( lastSpace + 1 ); + xoffset = w + info.paddingStart + ( xoffset - lastWidth ); + } else { + info.wraps.push_back( idx ); + xoffset = w + info.paddingStart; + } + lastSpace = 0; + } else if ( curChar == ' ' || curChar == '.' ) { + lastSpace = idx; + lastWidth = xoffset; + } + + idx++; } } @@ -177,10 +210,12 @@ bool LineWrapping::isWrapEnabled() const { return mConfig.mode != LineWrapMode::NoWrap; } -void LineWrapping::setMaxWidth( Float maxWidth ) { +void LineWrapping::setMaxWidth( Float maxWidth, bool forceReconstructBreaks ) { if ( maxWidth != mMaxWidth ) { mMaxWidth = maxWidth; reconstructBreaks(); + } else if ( forceReconstructBreaks ) { + reconstructBreaks(); } } @@ -199,17 +234,17 @@ void LineWrapping::setLineWrapMode( LineWrapMode mode ) { } TextPosition LineWrapping::getDocumentLine( Int64 visibleIndex ) const { - if ( mConfig.mode == LineWrapMode::NoWrap ) + if ( mConfig.mode == LineWrapMode::NoWrap || mWrappedLines.empty() ) return { visibleIndex, 0 }; return mWrappedLines[eeclamp( visibleIndex, 0ll, - static_cast( mWrappedLines.size() - 1 ) )]; + eemax( static_cast( mWrappedLines.size() ) - 1, 0ll ) )]; } Float LineWrapping::getLineOffset( Int64 docIdx ) const { - if ( mConfig.mode == LineWrapMode::NoWrap ) + if ( mConfig.mode == LineWrapMode::NoWrap || mWrappedLinesOffset.empty() ) return 0; - return mWrappedLinesOffset[eeclamp( docIdx, 0ll, - static_cast( mWrappedLinesOffset.size() - 1 ) )]; + return mWrappedLinesOffset[eeclamp( + docIdx, 0ll, eemax( static_cast( mWrappedLinesOffset.size() ) - 1, 0ll ) )]; } void LineWrapping::setConfig( Config config ) { @@ -220,7 +255,7 @@ void LineWrapping::setConfig( Config config ) { } void LineWrapping::reconstructBreaks() { - if ( 0 == mMaxWidth || mDoc->isLoading() ) + if ( 0 == mMaxWidth || !mDoc || mDoc->isLoading() ) return; mWrappedLines.clear(); @@ -335,6 +370,17 @@ TextRange LineWrapping::getVisualLineRange( Int64 visualLine ) const { return { start, end }; } +std::shared_ptr LineWrapping::getDocument() const { + return mDoc; +} + +void LineWrapping::setDocument( const std::shared_ptr& doc ) { + if ( mDoc != doc ) { + mDoc = doc; + reconstructBreaks(); + } +} + void LineWrapping::updateBreaks( Int64 fromLine, Int64 toLine, Int64 numLines ) { if ( mConfig.mode == LineWrapMode::NoWrap ) return; diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 9fd40c0d1..b5cb263f9 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -161,8 +161,6 @@ UICodeEditor::UICodeEditor( const std::string& elementTag, const bool& autoRegis mDoc->registerClient( this ); subscribeScheduledUpdate(); - mLineWrapping.setLineWrapMode( LineWrapMode::Word ); - if ( autoRegisterBaseCommands ) registerCommands(); if ( autoRegisterBaseKeybindings ) @@ -195,6 +193,7 @@ UICodeEditor::~UICodeEditor() { while ( mHighlightWordProcessing ) Sys::sleep( Milliseconds( 0.1 ) ); + mLineWrapping.setDocument( nullptr ); if ( mDoc.use_count() == 1 ) { DocEvent event( this, mDoc.get(), Event::OnDocumentClosed ); sendEvent( &event ); @@ -452,9 +451,6 @@ void UICodeEditor::reset() { TextDocument::LoadStatus UICodeEditor::loadFromFile( const std::string& path ) { auto ret = mDoc->loadFromFile( path ); if ( ret == TextDocument::LoadStatus::Loaded ) { - invalidateEditor(); - updateLongestLineWidth(); - invalidateDraw(); onDocumentLoaded(); } return ret; @@ -484,12 +480,9 @@ bool UICodeEditor::loadAsyncFromFile( } // if ( mLineWrapping.isWrapEnabled() ) - // mLineWrapping.reconstructBreaks(); + // mLineWrapping.setMaxWidth( getViewportWidth(), true ); runOnMainThread( [this, onLoaded, wasLocked, success] { - invalidateEditor(); - updateLongestLineWidth(); - invalidateDraw(); if ( !wasLocked ) setLocked( false ); onDocumentLoaded(); @@ -506,9 +499,6 @@ TextDocument::LoadStatus UICodeEditor::loadFromURL( const std::string& url, const Http::Request::FieldTable& headers ) { auto ret = mDoc->loadFromURL( url, headers ); if ( ret == TextDocument::LoadStatus::Loaded ) { - invalidateEditor(); - updateLongestLineWidth(); - invalidateDraw(); onDocumentLoaded(); } return ret; @@ -528,9 +518,6 @@ bool UICodeEditor::loadAsyncFromURL( runOnMainThread( [this] { invalidateDraw(); } ); } ); runOnMainThread( [this, success, onLoaded, wasLocked] { - invalidateEditor(); - updateLongestLineWidth(); - invalidateDraw(); if ( !wasLocked ) setLocked( false ); onDocumentLoaded(); @@ -596,22 +583,28 @@ void UICodeEditor::onFontStyleChanged() { invalidateDraw(); } -void UICodeEditor::onDocumentLoaded( TextDocument* ) {} +void UICodeEditor::onDocumentLoaded( TextDocument* ) { + if ( mInvalidateOnLoaded ) { + onDocumentLoaded(); + mInvalidateOnLoaded = false; + } +} void UICodeEditor::onDocumentReloaded( TextDocument* ) { DocEvent event( this, mDoc.get(), Event::OnDocumentReloaded ); sendEvent( &event ); invalidateDraw(); invalidateLongestLineWidth(); - mLineWrapping.reconstructBreaks(); + mLineWrapping.setMaxWidth( getViewportWidth(), true ); } void UICodeEditor::onDocumentLoaded() { DocEvent event( this, mDoc.get(), Event::OnDocumentLoaded ); sendEvent( &event ); + invalidateEditor(); invalidateDraw(); invalidateLongestLineWidth(); - mLineWrapping.reconstructBreaks(); + mLineWrapping.setMaxWidth( getViewportWidth(), true ); } void UICodeEditor::onDocumentChanged() { @@ -764,7 +757,7 @@ const Float& UICodeEditor::getLineNumberPaddingRight() const { } size_t UICodeEditor::getLineNumberDigits() const { - return eemax( 2UL, Math::countDigits( mDoc->linesCount() ) ); + return eemax( 2UL, Math::countDigits( (Int64)mDoc->linesCount() ) ); } Float UICodeEditor::getLineNumberWidth() const { @@ -879,14 +872,21 @@ TextDocument& UICodeEditor::getDocument() { void UICodeEditor::setDocument( std::shared_ptr doc ) { if ( mDoc.get() != doc.get() ) { mDoc->unregisterClient( this ); + mLineWrapping.setDocument( nullptr ); if ( mDoc.use_count() == 1 ) onDocumentClosed( mDoc.get() ); mDoc = doc; mDoc->registerClient( this ); - invalidateEditor(); - invalidateLongestLineWidth(); - invalidateDraw(); + mLineWrapping.setDocument( doc ); onDocumentChanged(); + if ( mDoc->isLoading() ) { + mInvalidateOnLoaded = true; + } else { + invalidateEditor(); + invalidateLongestLineWidth(); + invalidateDraw(); + mLineWrapping.setDocument( mDoc ); + } } } @@ -904,6 +904,33 @@ void UICodeEditor::invalidateLongestLineWidth() { mLongestLineWidthLastUpdate.restart(); } +void UICodeEditor::setLineWrapMode( LineWrapMode mode ) { + mLineWrapping.setLineWrapMode( mode ); +} + +LineWrapType UICodeEditor::getLineWrapType() const { + return mLineWrapType; +} + +void UICodeEditor::setLineWrapType( LineWrapType lineWrapType ) { + if ( mLineWrapType != lineWrapType ) { + mLineWrapType = lineWrapType; + switch ( mLineWrapType ) { + case LineWrapType::Viewport: + mLineWrapping.setMaxWidth( getViewportWidth() ); + case LineWrapType::LineBreakingColumn: + mLineWrapping.setMaxWidth( getGlyphWidth() * mLineBreakingColumn ); + break; + } + } +} + +void UICodeEditor::setLineWrapKeepIndentation( bool keep ) { + auto config = mLineWrapping.getConfig(); + config.keepIndentation = keep; + mLineWrapping.setConfig( config ); +} + Uint32 UICodeEditor::onFocus( NodeFocusReason reason ) { if ( !mLocked ) { mLastExecuteEventId = getUISceneNode()->getWindow()->getInput()->getEventsSentId(); @@ -1833,8 +1860,11 @@ void UICodeEditor::onDocumentSelectionChange( const Doc::TextRange& ) { sendCommonEvent( Event::OnSelectionChanged ); } -void UICodeEditor::onDocumentLineCountChange( const size_t&, const size_t& ) { +void UICodeEditor::onDocumentLineCountChange( const size_t& lastCount, const size_t& newCount ) { updateScrollBar(); + + if ( Math::countDigits( (Int64)lastCount ) != Math::countDigits( (Int64)newCount ) ) + mLineWrapping.setMaxWidth( getViewportWidth() ); } void UICodeEditor::onDocumentLineChanged( const Int64& lineNumber ) { @@ -2011,7 +2041,7 @@ bool UICodeEditor::isMinimapShown() const { void UICodeEditor::showMinimap( bool showMinimap ) { if ( showMinimap != mMinimapEnabled ) { mMinimapEnabled = showMinimap; - mLineWrapping.reconstructBreaks(); + mLineWrapping.setMaxWidth( getViewportWidth(), true ); } } @@ -2267,6 +2297,12 @@ void UICodeEditor::setLineBreakingColumn( const Uint32& lineBreakingColumn ) { if ( lineBreakingColumn != mLineBreakingColumn ) { mLineBreakingColumn = lineBreakingColumn; invalidateDraw(); + if ( mLineWrapType == LineWrapType::LineBreakingColumn ) { + if ( mLineBreakingColumn ) + mLineWrapping.setMaxWidth( getGlyphWidth() * mLineBreakingColumn ); + else + mLineWrapType = LineWrapType::Viewport; + } } } diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index b3af6630a..b7e053287 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -129,8 +129,7 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, editor.minimap = ini.getValueB( "editor", "minimap", true ); editor.showDocInfo = ini.getValueB( "editor", "show_doc_info", true ); editor.hideTabBarOnSingleTab = ini.getValueB( "editor", "hide_tab_bar_on_single_tab", false ); - editor.singleClickNavigation = - ini.getValueB( "editor", "single_click_tree_navigation", false ); + editor.singleClickNavigation = ini.getValueB( "editor", "single_click_tree_navigation", false ); editor.syncProjectTreeWithEditor = ini.getValueB( "editor", "sync_project_tree_with_editor", true ); editor.autoCloseXMLTags = ini.getValueB( "editor", "auto_close_xml_tags", true ); @@ -140,6 +139,12 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, editor.linesRelativePosition = ini.getValueB( "editor", "lines_relative_position", false ); editor.autoReloadOnDiskChange = ini.getValueB( "editor", "auto_reload_on_disk_change", false ); + editor.wrapMode = + LineWrapping::toLineWrapMode( ini.getValue( "editor", "wrap_mode", "nowrap" ) ); + editor.wrapType = + LineWrapping::toLineWrapType( ini.getValue( "editor", "wrap_type", "viewport" ) ); + editor.wrapKeepIndentation = ini.getValueB( "editor", "wrap_keep_indentation", true ); + searchBarConfig.caseSensitive = ini.getValueB( "search_bar", "case_sensitive", false ); searchBarConfig.luaPattern = ini.getValueB( "search_bar", "lua_pattern", false ); searchBarConfig.wholeWord = ini.getValueB( "search_bar", "whole_word", false ); @@ -265,6 +270,10 @@ void AppConfig::save( const std::vector& recentFiles, ini.setValueB( "editor", "lines_relative_position", editor.linesRelativePosition ); ini.setValueB( "editor", "auto_reload_on_disk_change", editor.autoReloadOnDiskChange ); + ini.setValue( "editor", "wrap_mode", LineWrapping::fromLineWrapMode( editor.wrapMode ) ); + ini.setValue( "editor", "wrap_type", LineWrapping::fromLineWrapType( editor.wrapType ) ); + ini.setValueB( "editor", "wrap_keep_indentation", editor.wrapKeepIndentation ); + ini.setValueB( "search_bar", "case_sensitive", searchBarConfig.caseSensitive ); ini.setValueB( "search_bar", "lua_pattern", searchBarConfig.luaPattern ); ini.setValueB( "search_bar", "whole_word", searchBarConfig.wholeWord ); diff --git a/src/tools/ecode/appconfig.hpp b/src/tools/ecode/appconfig.hpp index cec6c1dba..aa58ee6b2 100644 --- a/src/tools/ecode/appconfig.hpp +++ b/src/tools/ecode/appconfig.hpp @@ -76,6 +76,9 @@ struct CodeEditorConfig { bool autoCloseXMLTags{ true }; bool linesRelativePosition{ false }; bool autoReloadOnDiskChange{ false }; + LineWrapMode wrapMode{ LineWrapMode::NoWrap }; + LineWrapType wrapType{ LineWrapType::Viewport }; + bool wrapKeepIndentation{ true }; std::string autoCloseBrackets{ "" }; Time cursorBlinkingTime{ Seconds( 0.5f ) }; }; diff --git a/src/tools/ecode/plugins/pluginmanager.cpp b/src/tools/ecode/plugins/pluginmanager.cpp index 10b8ce56a..82b94865d 100644 --- a/src/tools/ecode/plugins/pluginmanager.cpp +++ b/src/tools/ecode/plugins/pluginmanager.cpp @@ -1,6 +1,6 @@ +#include "pluginmanager.hpp" #include "../filesystemlistener.hpp" #include "plugin.hpp" -#include "pluginmanager.hpp" #include #include #include @@ -26,6 +26,10 @@ PluginManager::~PluginManager() { unsubscribeFileSystemListener(); } +bool PluginManager::isClosing() const { + return mClosing; +} + void PluginManager::registerPlugin( const PluginDefinition& def ) { mDefinitions[def.id] = def; } diff --git a/src/tools/ecode/plugins/pluginmanager.hpp b/src/tools/ecode/plugins/pluginmanager.hpp index 56c39b944..9bbac722d 100644 --- a/src/tools/ecode/plugins/pluginmanager.hpp +++ b/src/tools/ecode/plugins/pluginmanager.hpp @@ -348,6 +348,8 @@ class PluginManager { void unsubscribeFileSystemListener( Plugin* plugin ); + bool isClosing() const; + protected: using SubscribedPlugins = std::map>; diff --git a/src/tools/ecode/settingsmenu.cpp b/src/tools/ecode/settingsmenu.cpp index c3eb82b18..416a7516c 100644 --- a/src/tools/ecode/settingsmenu.cpp +++ b/src/tools/ecode/settingsmenu.cpp @@ -1229,6 +1229,92 @@ UIMenu* SettingsMenu::createRendererMenu() { UIMenu* SettingsMenu::createViewMenu() { mViewMenu = UIPopUpMenu::New(); + mLineWrapMenu = UIPopUpMenu::New(); + + mViewMenu->addSubMenu( i18n( "line_wrap", "Line Wrap" ), nullptr, mLineWrapMenu ) + ->on( Event::OnMenuShow, [this]( auto ) { + if ( mLineWrapMenu->getCount() == 0 ) { + UIPopUpMenu* wrapModeMenu = UIPopUpMenu::New(); + wrapModeMenu->addRadioButton( i18n( "no_wrap", "No Wrap" ) ) + ->setId( LineWrapping::fromLineWrapMode( LineWrapMode::NoWrap ) ); + wrapModeMenu->addRadioButton( i18n( "wrap_word", "Word wrap" ) ) + ->setId( LineWrapping::fromLineWrapMode( LineWrapMode::Word ) ); + wrapModeMenu->addRadioButton( i18n( "wrap_letter", "Letter wrap" ) ) + ->setId( LineWrapping::fromLineWrapMode( LineWrapMode::Letter ) ); + + wrapModeMenu->on( Event::OnItemClicked, [this]( const Event* event ) { + if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) ) + return; + UIMenuItem* item = event->getNode()->asType(); + LineWrapMode mode = LineWrapping::toLineWrapMode( item->getId() ); + mApp->getConfig().editor.wrapMode = mode; + mApp->getSplitter()->forEachEditor( + [mode]( UICodeEditor* editor ) { editor->setLineWrapMode( mode ); } ); + } ); + + mLineWrapMenu->addSubMenu( i18n( "wrap_mode", "Wrap Mode" ), nullptr, wrapModeMenu ) + ->setId( "wrap_mode" ); + + UIPopUpMenu* wrapTypeMenu = UIPopUpMenu::New(); + wrapTypeMenu->addRadioButton( i18n( "viewport", "Viewport" ) ) + ->setId( LineWrapping::fromLineWrapType( LineWrapType::Viewport ) ); + wrapTypeMenu + ->addRadioButton( i18n( "line_breaking_column", "Line Breaking Column" ) ) + ->setId( LineWrapping::fromLineWrapType( LineWrapType::LineBreakingColumn ) ); + + wrapTypeMenu->on( Event::OnItemClicked, [this]( const Event* event ) { + if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) ) + return; + UIMenuItem* item = event->getNode()->asType(); + LineWrapType type = LineWrapping::toLineWrapType( item->getId() ); + mApp->getConfig().editor.wrapType = type; + mApp->getSplitter()->forEachEditor( + [type]( UICodeEditor* editor ) { editor->setLineWrapType( type ); } ); + } ); + + mLineWrapMenu + ->addSubMenu( i18n( "wrap_type_ellipsis", "Wrap Against..." ), nullptr, + wrapTypeMenu ) + ->setId( "wrap_type" ); + + mLineWrapMenu->addCheckBox( i18n( "keep_indentation", "Keep Indentation" ) ) + ->setId( "keep_indentation" ); + + mLineWrapMenu->on( Event::OnItemClicked, [this]( const Event* event ) { + if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) ) + return; + UIMenuItem* item = event->getNode()->asType(); + + if ( "keep_indentation" == item->getId() ) { + bool keep = item->asType()->isActive(); + mApp->getConfig().editor.wrapKeepIndentation = keep; + mApp->getSplitter()->forEachEditor( [keep]( UICodeEditor* editor ) { + editor->setLineWrapKeepIndentation( keep ); + } ); + } + } ); + } + + auto* wrapModeMenu = + mLineWrapMenu->find( "wrap_mode" )->asType()->getSubMenu(); + auto* wrapTypeMenu = + mLineWrapMenu->find( "wrap_type" )->asType()->getSubMenu(); + UIMenuCheckBox* wrapKeepIndentation = + mLineWrapMenu->find( "keep_indentation" )->asType(); + + const auto& cfg = mApp->getConfig(); + + wrapModeMenu->find( LineWrapping::fromLineWrapMode( cfg.editor.wrapMode ) ) + ->asType() + ->setActive( true ); + + wrapTypeMenu->find( LineWrapping::fromLineWrapType( cfg.editor.wrapType ) ) + ->asType() + ->setActive( true ); + + wrapKeepIndentation->setActive( cfg.editor.wrapKeepIndentation ); + } ); + mViewMenu->addCheckBox( i18n( "show_line_numbers", "Show Line Numbers" ) ) ->setActive( mApp->getConfig().editor.showLineNumbers ) ->setId( "show-line-numbers" ); diff --git a/src/tools/ecode/settingsmenu.hpp b/src/tools/ecode/settingsmenu.hpp index 5a495c381..5c667c3e0 100644 --- a/src/tools/ecode/settingsmenu.hpp +++ b/src/tools/ecode/settingsmenu.hpp @@ -111,6 +111,7 @@ class SettingsMenu { UIPopUpMenu* mProjectMenu{ nullptr }; UIPopUpMenu* mEditMenu{ nullptr }; UIPopUpMenu* mHelpMenu{ nullptr }; + UIPopUpMenu* mLineWrapMenu{ nullptr }; UIMenuBar* mMenuBar{ nullptr }; std::vector mFileTypeMenues; Float mFileTypeMenuesCreatedWithHeight{ 0 };