UISceneNode: Added implementation of getTranslator().

UICodeEditor: Improved minimap dragging.
SyntaxDefinitionManager: Fixed link pattern.
ecode: Added per project document configuration.
This commit is contained in:
Martín Lucas Golini
2022-04-28 00:38:31 -03:00
parent 60650407c6
commit d3bcc824ca
9 changed files with 413 additions and 86 deletions

View File

@@ -46,6 +46,8 @@ class EE_API UISceneNode : public SceneNode {
void setTranslator( Translator translator );
const Translator& getTranslator() const;
Translator& getTranslator();
String getTranslatorString( const std::string& str );

View File

@@ -162,6 +162,7 @@ void Text::setString( const String& string ) {
mColorsNeedUpdate = true;
mGeometryNeedUpdate = true;
mCachedWidthNeedUpdate = true;
mContainsColorEmoji = false;
if ( FontManager::instance()->getColorEmojiFont() != nullptr ) {
if ( mFont->getType() == FontType::TTF ) {
FontTrueType* fontTrueType = static_cast<FontTrueType*>( mFont );

View File

@@ -1004,8 +1004,7 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() {
{ { "^%[.-%]" }, "keyword2" },
{ { "%s%[.-%]" }, "keyword2" },
{ { "=" }, "operator" },
{ { "https?://(([%w_.~!*:@&+$/?%%#-]-)(%w[-.%w]*%.)(%w%w%w?%w?)(:?)(%d*)(/"
"?)([%w_.~!*:@&+$/?%%#=-]*))" },
{ { "https?://[%w_.~!*:@&+$/?%%#-]-%w[-.%w]*%.%w%w%w?%w?:?%d*/?[%w_.~!*:@&+$/?%%#=-]*" },
"link" },
{ { "[a-z]+" }, "symbol" } },
{ { "true", "literal" }, { "false", "literal" } },
@@ -1888,8 +1887,7 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() {
{ { "%f[%S]%-?%d+[%d%.eE]*f?" }, "number" },
{ { "%f[%S]%-?%.?%d+f?" }, "number" },
{ { "!!float", "\n", "\\" }, "number" },
{ { "https?://(([%w_.~!*:@&+$/?%%#-]-)(%w[-.%w]*%.)(%w%w%w?%w?)(:?)(%d*)(/"
"?)([%w_.~!*:@&+$/?%%#=-]*))" },
{ { "https?://[%w_.~!*:@&+$/?%%#-]-%w[-.%w]*%.%w%w%w?%w?:?%d*/?[%w_.~!*:@&+$/?%%#=-]*" },
"link" },
{ { "%-%-%-" }, "literal" },
},

View File

@@ -312,6 +312,7 @@ void UICodeEditor::scheduledUpdate( const Time& ) {
if ( !( getUISceneNode()->getWindow()->getInput()->getPressTrigger() & EE_BUTTON_LMASK ) ) {
if ( mMinimapDragging ) {
mMinimapDragging = false;
getEventDispatcher()->setNodeDragging( NULL );
mVScrollBar->setEnabled( true );
getUISceneNode()->setCursor( Cursor::Arrow );
updateMipmapHover( getUISceneNode()->getWindow()->getInput()->getMousePosf() );
@@ -954,8 +955,8 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags
if ( mMinimapEnabled ) {
Rectf rect( getMinimapRect( getScreenStart() ) );
if ( ( flags & EE_BUTTON_LMASK ) && !mVScrollBar->isDragging() && !mMinimapDragging &&
rect.contains( position.asFloat() ) ) {
if ( ( flags & EE_BUTTON_LMASK ) && !getEventDispatcher()->isNodeDragging() &&
!mMinimapDragging && rect.contains( position.asFloat() ) ) {
if ( mMouseDown )
return 1;
updateMipmapHover( position.asFloat() );
@@ -968,9 +969,9 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags
mMinimapScrollOffset =
calculateMinimapClickedLine( position ) - getVisibleLineRange().first;
mMinimapDragging = true;
getUISceneNode()->getWindow()->getInput()->captureMouse( true );
getUISceneNode()->setCursor( Cursor::Arrow );
getEventDispatcher()->setNodeDragging( this );
mVScrollBar->setEnabled( false );
getUISceneNode()->setCursor( Cursor::Arrow );
return 1;
} else if ( mMinimapDragging ) {
if ( mMouseDown ) {
@@ -1081,6 +1082,7 @@ Uint32 UICodeEditor::onMouseUp( const Vector2i& position, const Uint32& flags )
if ( flags & EE_BUTTON_LMASK ) {
if ( mMinimapDragging ) {
mMinimapDragging = false;
getEventDispatcher()->setNodeDragging( NULL );
mVScrollBar->setEnabled( true );
getUISceneNode()->setCursor( Cursor::Arrow );
updateMipmapHover( position.asFloat() );

View File

@@ -106,6 +106,14 @@ void UISceneNode::setTranslator( Translator translator ) {
mTranslator = translator;
}
const Translator& UISceneNode::getTranslator() const {
return mTranslator;
}
Translator& UISceneNode::getTranslator() {
return mTranslator;
}
String UISceneNode::getTranslatorString( const std::string& str ) {
if ( String::startsWith( str, "@string/" ) ) {
String tstr = mTranslator.getString( str.substr( 8 ) );

View File

@@ -190,7 +190,8 @@ struct ProjectPath {
};
void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter,
const std::string& configPath ) {
const std::string& configPath,
const ProjectDocumentConfig& docConfig ) {
FileSystem::dirAddSlashAtEnd( projectFolder );
std::vector<UICodeEditor*> editors = editorSplitter->getAllEditors();
std::vector<ProjectPath> paths;
@@ -211,11 +212,23 @@ void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* ed
!editorSplitter->getTabWidgets().empty()
? editorSplitter->getTabWidgets()[0]->getTabSelectedIndex()
: 0 );
ini.setValueB( "document", "use_global_settings", docConfig.useGlobalSettings );
ini.setValueB( "document", "trim_trailing_whitespaces", docConfig.doc.trimTrailingWhitespaces );
ini.setValueB( "document", "force_new_line_at_end_of_file",
docConfig.doc.forceNewLineAtEndOfFile );
ini.setValueB( "document", "auto_detect_indent_type", docConfig.doc.autoDetectIndentType );
ini.setValueB( "document", "write_bom", docConfig.doc.writeUnicodeBOM );
ini.setValueI( "document", "indent_width", docConfig.doc.indentWidth );
ini.setValueB( "document", "indent_spaces", docConfig.doc.indentSpaces );
ini.setValueB( "document", "windows_line_endings", docConfig.doc.windowsLineEndings );
ini.setValueI( "document", "tab_width", docConfig.doc.tabWidth );
ini.setValueI( "document", "line_breaking_column", docConfig.doc.lineBreakingColumn );
ini.writeFile();
}
void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter,
const std::string& configPath, std::shared_ptr<ThreadPool> pool ) {
const std::string& configPath, ProjectDocumentConfig& docConfig,
std::shared_ptr<ThreadPool> pool ) {
FileSystem::dirAddSlashAtEnd( projectFolder );
std::string projectsPath( configPath + "projects" + FileSystem::getOSSlash() );
MD5::Result hash = MD5::fromString( projectFolder );
@@ -253,4 +266,19 @@ void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* ed
editorSplitter->switchToTab( currentPage );
} );
}
docConfig.useGlobalSettings = ini.getValueB( "document", "use_global_settings", true );
docConfig.doc.trimTrailingWhitespaces =
ini.getValueB( "document", "trim_trailing_whitespaces", false );
docConfig.doc.forceNewLineAtEndOfFile =
ini.getValueB( "document", "force_new_line_at_end_of_file", false );
docConfig.doc.autoDetectIndentType =
ini.getValueB( "document", "auto_detect_indent_type", true );
docConfig.doc.writeUnicodeBOM = ini.getValueB( "document", "write_bom", false );
docConfig.doc.indentWidth = ini.getValueI( "document", "indent_width", 4 );
docConfig.doc.indentSpaces = ini.getValueB( "document", "indent_spaces", false );
docConfig.doc.windowsLineEndings = ini.getValueB( "document", "windows_line_endings", false );
docConfig.doc.tabWidth = eemax( 2, ini.getValueI( "document", "tab_width", 4 ) );
docConfig.doc.lineBreakingColumn =
eemax( 0, ini.getValueI( "document", "line_breaking_column", 100 ) );
}

View File

@@ -73,6 +73,13 @@ struct DocumentConfig {
int lineBreakingColumn{ 100 };
};
struct ProjectDocumentConfig {
bool useGlobalSettings{ true };
DocumentConfig doc;
ProjectDocumentConfig() {}
ProjectDocumentConfig( const DocumentConfig& doc ) { this->doc = doc; }
};
struct AppConfig {
WindowConfig window;
CodeEditorConfig editor;
@@ -92,10 +99,11 @@ struct AppConfig {
EE::Window::Window* win, const std::string& colorSchemeName );
void saveProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter,
const std::string& configPath );
const std::string& configPath, const ProjectDocumentConfig& docConfig );
void loadProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter,
const std::string& configPath, std::shared_ptr<ThreadPool> pool );
const std::string& configPath, ProjectDocumentConfig& docConfig,
std::shared_ptr<ThreadPool> pool );
};
#endif // APPCONFIG_HPP

View File

@@ -16,20 +16,24 @@ bool App::onCloseRequestCallback( EE::Window::Window* ) {
mEditorSplitter->getCurEditor()->isDirty() ) {
UIMessageBox* msgBox = UIMessageBox::New(
UIMessageBox::OK_CANCEL,
"Do you really want to close the code editor?\nAll changes will be lost." );
i18n( "confirm_ecode_exit",
"Do you really want to close the code editor?\nAll changes will be lost." )
.unescape() );
msgBox->addEventListener( Event::MsgBoxConfirmClick, [&]( const Event* ) {
if ( !mCurrentProject.empty() )
mConfig.saveProject( mCurrentProject, mEditorSplitter, mConfigPath );
mConfig.saveProject( mCurrentProject, mEditorSplitter, mConfigPath,
mProjectDocConfig );
mWindow->close();
} );
msgBox->addEventListener( Event::OnClose, [&]( const Event* ) { msgBox = nullptr; } );
msgBox->setTitle( "Close " + mWindowTitle + "?" );
msgBox->setTitle( String::format( i18n( "close_title", "Close %s?" ).toUtf8().c_str(),
mWindowTitle.c_str() ) );
msgBox->center();
msgBox->showWhenReady();
return false;
} else {
if ( !mCurrentProject.empty() )
mConfig.saveProject( mCurrentProject, mEditorSplitter, mConfigPath );
mConfig.saveProject( mCurrentProject, mEditorSplitter, mConfigPath, mProjectDocConfig );
return true;
}
}
@@ -135,7 +139,7 @@ void App::openFileDialog() {
UIFileDialog* dialog = UIFileDialog::New( UIFileDialog::DefaultFlags, "*",
mLastFileFolder.empty() ? "." : mLastFileFolder );
dialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL );
dialog->setTitle( "Open File" );
dialog->setTitle( i18n( "open_file", "Open File" ) );
dialog->setCloseShortcut( KEY_ESCAPE );
dialog->addEventListener( Event::OpenFile, [&]( const Event* event ) {
auto file = event->getNode()->asType<UIFileDialog>()->getFullPath();
@@ -157,7 +161,7 @@ void App::openFolderDialog() {
UIFileDialog::ShowOnlyFolders,
"*", "." );
dialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL );
dialog->setTitle( "Open Folder" );
dialog->setTitle( i18n( "open_folder", "Open Folder" ) );
dialog->setCloseShortcut( KEY_ESCAPE );
dialog->addEventListener( Event::OpenFile, [&]( const Event* event ) {
String path( event->getNode()->asType<UIFileDialog>()->getFullPath() );
@@ -699,7 +703,6 @@ UIMenu* App::createViewMenu() {
->setActive( mConfig.editor.syncProjectTreeWithEditor )
->setTooltipText( "Syncronizes the current focused document as the selected\nfile in the "
"directory tree." );
mViewMenu->add( "Line Breaking Column" );
mViewMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) )
@@ -774,25 +777,6 @@ UIMenu* App::createViewMenu() {
mProjectTreeView->setSingleClickNavigation( mConfig.editor.singleClickTreeNavigation );
} else if ( item->getText() == "Synchronize project tree with editor" ) {
mConfig.editor.syncProjectTreeWithEditor = item->asType<UIMenuCheckBox>()->isActive();
} else if ( item->getText() == "Line Breaking Column" ) {
UIMessageBox* msgBox =
UIMessageBox::New( UIMessageBox::INPUT, "Set Line Breaking Column:\n"
"Set 0 to disable it.\n" );
msgBox->setTitle( mWindowTitle );
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->getTextInput()->setAllowOnlyNumbers( true, false );
msgBox->getTextInput()->setText( String::toString( mConfig.doc.lineBreakingColumn ) );
msgBox->showWhenReady();
msgBox->addEventListener( Event::MsgBoxConfirmClick, [&, msgBox]( const Event* ) {
int val;
if ( String::fromString( val, msgBox->getTextInput()->getText() ) && val >= 0 ) {
mConfig.doc.lineBreakingColumn = val;
mEditorSplitter->forEachEditor(
[val]( UICodeEditor* editor ) { editor->setLineBreakingColumn( val ); } );
msgBox->closeWindow();
}
} );
setFocusEditorOnClose( msgBox );
} else {
String text = String( event->getNode()->asType<UIMenuItem>()->getText() ).toLower();
String::replaceAll( text, " ", "-" );
@@ -817,8 +801,8 @@ Drawable* App::findIcon( const std::string& name ) {
return nullptr;
}
String App::i18n( const std::string& name, const String& def ) {
return mUISceneNode->getTranslatorString( name, def );
String App::i18n( const std::string& key, const String& def ) {
return mUISceneNode->getTranslatorStringFromKey( key, def );
}
UIMenu* App::createEditMenu() {
@@ -842,15 +826,16 @@ UIMenu* App::createEditMenu() {
getKeybind( "find-replace" ) )
->setId( "find-replace" );
menu->addSeparator();
menu->add( i18n( "key_bindings", "Key Bindings" ), findIcon( "keybindings" ),
getKeybind( "keybindings" ) )
->setId( "keybindings" );
menu->add( i18n( "open_containing_folder", "Open Containing Folder..." ),
findIcon( "folder-open" ), getKeybind( "open-containing-folder" ) )
->setId( "open-containing-folder" );
menu->add( i18n( "copy_file_path", "Copy File Path" ), findIcon( "copy" ),
getKeybind( "copy-file-path" ) )
->setId( "copy-file-path" );
menu->addSeparator();
menu->add( i18n( "key_bindings", "Key Bindings" ), findIcon( "keybindings" ),
getKeybind( "keybindings" ) )
->setId( "keybindings" );
menu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) )
return;
@@ -882,18 +867,25 @@ makeAutoClosePairs( const std::string& strPairs ) {
}
UIMenu* App::createDocumentMenu() {
auto shouldCloseCb = []( UIMenuItem* ) -> bool { return false; };
mDocMenu = UIPopUpMenu::New();
// **** CURRENT DOCUMENT ****
mDocMenu->add( "Current Document" )->setTextAlign( UI_HALIGN_CENTER );
mDocMenu->add( i18n( "current_document", "Current Document" ) )
->setTextAlign( UI_HALIGN_CENTER );
mDocMenu->addCheckBox( "Auto Detect Indent Type & Width", mConfig.doc.autoDetectIndentType )
mDocMenu
->addCheckBox(
i18n( "auto_detect_indent_type_and_width", "Auto Detect Indent Type & Width" ),
mConfig.doc.autoDetectIndentType )
->setId( "auto_indent_cur" );
UIPopUpMenu* tabTypeMenu = UIPopUpMenu::New();
tabTypeMenu->addRadioButton( "Tabs" )->setId( "tabs" );
tabTypeMenu->addRadioButton( "Spaces" )->setId( "spaces" );
mDocMenu->addSubMenu( "Indentation Type", nullptr, tabTypeMenu )->setId( "indent_type_cur" );
tabTypeMenu->addRadioButton( i18n( "tabs", "Tabs" ) )->setId( "tabs" );
tabTypeMenu->addRadioButton( i18n( "spaces", "Spaces" ) )->setId( "spaces" );
mDocMenu->addSubMenu( i18n( "indentation_type", "Indentation Type" ), nullptr, tabTypeMenu )
->setId( "indent_type_cur" );
tabTypeMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
const String& text = event->getNode()->asType<UIMenuRadioButton>()->getId();
if ( mEditorSplitter->getCurEditor() ) {
@@ -913,7 +905,8 @@ UIMenu* App::createDocumentMenu() {
w )
->setId( String::format( "indent_width_%zu", w ) )
->setData( w );
mDocMenu->addSubMenu( "Indent Width", nullptr, indentWidthMenu )->setId( "indent_width_cur" );
mDocMenu->addSubMenu( i18n( "indent_width", "Indent Width" ), nullptr, indentWidthMenu )
->setId( "indent_width_cur" );
indentWidthMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
if ( mEditorSplitter->getCurEditor() ) {
int width = event->getNode()->getData();
@@ -929,7 +922,8 @@ UIMenu* App::createDocumentMenu() {
mEditorSplitter->getCurEditor()->getTabWidth() == w )
->setId( String::format( "tab_width_%zu", w ) )
->setData( w );
mDocMenu->addSubMenu( "Tab Width", nullptr, tabWidthMenu )->setId( "tab_width_cur" );
mDocMenu->addSubMenu( i18n( "tab_width", "Tab Width" ), nullptr, tabWidthMenu )
->setId( "tab_width_cur" );
tabWidthMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
if ( mEditorSplitter->getCurEditor() ) {
int width = event->getNode()->getData();
@@ -942,7 +936,8 @@ UIMenu* App::createDocumentMenu() {
->setId( "windows" );
lineEndingsMenu->addRadioButton( "Unix (LF)", !mConfig.doc.windowsLineEndings )
->setId( "unix" );
mDocMenu->addSubMenu( "Line Endings", nullptr, lineEndingsMenu )->setId( "line_endings_cur" );
mDocMenu->addSubMenu( i18n( "line_endings", "Line Endings" ), nullptr, lineEndingsMenu )
->setId( "line_endings_cur" );
lineEndingsMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
bool winLe = event->getNode()->asType<UIRadioButton>()->getId() == "windows";
if ( mEditorSplitter->getCurEditor() ) {
@@ -952,19 +947,23 @@ UIMenu* App::createDocumentMenu() {
}
} );
mDocMenu->addCheckBox( "Read Only" )->setId( "read_only" );
mDocMenu->addCheckBox( i18n( "read_only", "Read Only" ) )->setId( "read_only" );
mDocMenu->addCheckBox( "Trim Trailing Whitespaces", mConfig.doc.trimTrailingWhitespaces )
mDocMenu
->addCheckBox( i18n( "trim_trailing_whitespaces", "Trim Trailing Whitespaces" ),
mConfig.doc.trimTrailingWhitespaces )
->setId( "trim_whitespaces_cur" );
mDocMenu->addCheckBox( "Force New Line at End of File", mConfig.doc.forceNewLineAtEndOfFile )
mDocMenu
->addCheckBox( i18n( "force_new_line_at_end_of_file", "Force New Line at End of File" ),
mConfig.doc.forceNewLineAtEndOfFile )
->setId( "force_nl_cur" );
mDocMenu->addCheckBox( "Write Unicode BOM", mConfig.doc.writeUnicodeBOM )
mDocMenu
->addCheckBox( i18n( "write_unicode_bom", "Write Unicode BOM" ),
mConfig.doc.writeUnicodeBOM )
->setId( "write_bom_cur" );
mDocMenu->addSeparator();
mDocMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
if ( !mEditorSplitter->getCurEditor() ||
event->getNode()->isType( UI_TYPE_MENU_SEPARATOR ) ||
@@ -989,21 +988,28 @@ UIMenu* App::createDocumentMenu() {
}
} );
// **** GLOBAL OPTIONS ****
UIPopUpMenu* globalMenu = UIPopUpMenu::New();
mDocMenu->addSubMenu( "Global Settings", findIcon( "global_settings" ), globalMenu );
// **** GLOBAL SETTINGS ****
mDocMenu->addSeparator();
globalMenu->addCheckBox( "Auto Detect Indent Type & Width", mConfig.doc.autoDetectIndentType )
UIPopUpMenu* globalMenu = UIPopUpMenu::New();
mDocMenu->addSubMenu( i18n( "global_settings", "Global Settings" ),
findIcon( "global-settings" ), globalMenu );
globalMenu
->addCheckBox(
i18n( "auto_detect_indent_type_and_width", "Auto Detect Indent Type & Width" ),
mConfig.doc.autoDetectIndentType )
->setId( "auto_indent" );
UIPopUpMenu* tabTypeMenuGlobal = UIPopUpMenu::New();
tabTypeMenuGlobal->addRadioButton( "Tabs" )
tabTypeMenuGlobal->addRadioButton( i18n( "tabs", "Tabs" ) )
->setActive( !mConfig.doc.indentSpaces )
->setId( "tabs" );
tabTypeMenuGlobal->addRadioButton( "Spaces" )
tabTypeMenuGlobal->addRadioButton( i18n( "spaces", "Spaces" ) )
->setActive( mConfig.doc.indentSpaces )
->setId( "spaces" );
globalMenu->addSubMenu( "Indentation Type", nullptr, tabTypeMenuGlobal )
globalMenu
->addSubMenu( i18n( "indentation_type", "Indentation Type" ), nullptr, tabTypeMenuGlobal )
->setId( "indent_type" );
tabTypeMenuGlobal->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
const String& text = event->getNode()->asType<UIMenuRadioButton>()->getId();
@@ -1015,7 +1021,7 @@ UIMenu* App::createDocumentMenu() {
indentWidthMenuGlobal->addRadioButton( String::toString( w ), mConfig.doc.indentWidth == w )
->setId( String::format( "indent_width_%d", w ) )
->setData( w );
globalMenu->addSubMenu( "Indent Width", nullptr, indentWidthMenuGlobal )
globalMenu->addSubMenu( i18n( "indent_width", "Indent Width" ), nullptr, indentWidthMenuGlobal )
->setId( "indent_width" );
indentWidthMenuGlobal->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
int width = event->getNode()->getData();
@@ -1027,7 +1033,8 @@ UIMenu* App::createDocumentMenu() {
tabWidthMenuGlobal->addRadioButton( String::toString( w ), mConfig.doc.tabWidth == w )
->setId( String::format( "tab_width_%d", w ) )
->setData( w );
globalMenu->addSubMenu( "Tab Width", nullptr, tabWidthMenuGlobal )->setId( "tab_width_cur" );
globalMenu->addSubMenu( i18n( "tab_width", "Tab Width" ), nullptr, tabWidthMenuGlobal )
->setId( "tab_width_cur" );
tabWidthMenuGlobal->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
int width = event->getNode()->getData();
mConfig.doc.tabWidth = width;
@@ -1038,7 +1045,7 @@ UIMenu* App::createDocumentMenu() {
->setId( "windows" );
lineEndingsGlobalMenu->addRadioButton( "Unix (LF)", !mConfig.doc.windowsLineEndings )
->setId( "unix" );
globalMenu->addSubMenu( "Line Endings", nullptr, lineEndingsGlobalMenu )
globalMenu->addSubMenu( i18n( "line_endings", "Line Endings" ), nullptr, lineEndingsGlobalMenu )
->setId( "line_endings" );
lineEndingsGlobalMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
bool winLe = event->getNode()->asType<UIRadioButton>()->getId() == "windows";
@@ -1046,30 +1053,42 @@ UIMenu* App::createDocumentMenu() {
} );
UIPopUpMenu* bracketsMenu = UIPopUpMenu::New();
globalMenu->addSubMenu( "Auto-Close Brackets & Tags", nullptr, bracketsMenu );
globalMenu->addSubMenu( i18n( "auto_close_brackets_and_tags", "Auto-Close Brackets & Tags" ),
nullptr, bracketsMenu );
auto& closeBrackets = mConfig.editor.autoCloseBrackets;
auto shouldCloseCb = []( UIMenuItem* ) -> bool { return false; };
bracketsMenu->addCheckBox( "Brackets ()", closeBrackets.find( '(' ) != std::string::npos )
bracketsMenu
->addCheckBox( i18n( "brackets", "Brackets ()" ),
closeBrackets.find( '(' ) != std::string::npos )
->setOnShouldCloseCb( shouldCloseCb )
->setId( "()" );
bracketsMenu->addCheckBox( "Curly Brackets {}", closeBrackets.find( '{' ) != std::string::npos )
bracketsMenu
->addCheckBox( i18n( "curly_brackets", "Curly Brackets {}" ),
closeBrackets.find( '{' ) != std::string::npos )
->setOnShouldCloseCb( shouldCloseCb )
->setId( "{}" );
bracketsMenu
->addCheckBox( "Square Brackets []", closeBrackets.find( '[' ) != std::string::npos )
->addCheckBox( i18n( "square_brakcets", "Square Brackets []" ),
closeBrackets.find( '[' ) != std::string::npos )
->setOnShouldCloseCb( shouldCloseCb )
->setId( "[]" );
bracketsMenu->addCheckBox( "Single Quotes ''", closeBrackets.find( '\'' ) != std::string::npos )
bracketsMenu
->addCheckBox( i18n( "single_quotes", "Single Quotes ''" ),
closeBrackets.find( '\'' ) != std::string::npos )
->setOnShouldCloseCb( shouldCloseCb )
->setId( "''" );
bracketsMenu
->addCheckBox( "Double Quotes \"\"", closeBrackets.find( '"' ) != std::string::npos )
->addCheckBox( i18n( "double_quotes", "Double Quotes \"\"" ),
closeBrackets.find( '"' ) != std::string::npos )
->setOnShouldCloseCb( shouldCloseCb )
->setId( "\"\"" );
bracketsMenu->addCheckBox( "Back Quotes ``", closeBrackets.find( '`' ) != std::string::npos )
bracketsMenu
->addCheckBox( i18n( "back_quotes", "Back Quotes ``" ),
closeBrackets.find( '`' ) != std::string::npos )
->setOnShouldCloseCb( shouldCloseCb )
->setId( "``" );
bracketsMenu->addCheckBox( "Auto Close XML Tags", mConfig.editor.autoCloseXMLTags )
bracketsMenu
->addCheckBox( i18n( "auto_close_xml_tags", "Auto Close XML Tags" ),
mConfig.editor.autoCloseXMLTags )
->setOnShouldCloseCb( shouldCloseCb )
->setId( "XML" );
bracketsMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
@@ -1100,15 +1119,26 @@ UIMenu* App::createDocumentMenu() {
}
} );
globalMenu->addCheckBox( "Trim Trailing Whitespaces", mConfig.doc.trimTrailingWhitespaces )
globalMenu
->addCheckBox( i18n( "trim_trailing_whitespaces", "Trim Trailing Whitespaces" ),
mConfig.doc.trimTrailingWhitespaces )
->setId( "trim_whitespaces" );
globalMenu->addCheckBox( "Force New Line at End of File", mConfig.doc.forceNewLineAtEndOfFile )
globalMenu
->addCheckBox( i18n( "force_new_line_at_end_of_file", "Force New Line at End of File" ),
mConfig.doc.forceNewLineAtEndOfFile )
->setId( "force_nl" );
globalMenu->addCheckBox( "Write Unicode BOM", mConfig.doc.writeUnicodeBOM )
globalMenu
->addCheckBox( i18n( "write_unicode_bom", "Write Unicode BOM" ),
mConfig.doc.writeUnicodeBOM )
->setId( "write_bom" );
globalMenu->addSeparator();
globalMenu->add( i18n( "line_breaking_column", "Line Breaking Column" ) )
->setId( "line_breaking_column" );
globalMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
if ( !mEditorSplitter->getCurEditor() ||
event->getNode()->isType( UI_TYPE_MENU_SEPARATOR ) ||
@@ -1127,12 +1157,250 @@ UIMenu* App::createDocumentMenu() {
} else if ( "auto_indent" == id ) {
mConfig.doc.autoDetectIndentType = item->isActive();
}
} else if ( "line_breaking_column" == id ) {
UIMessageBox* msgBox = UIMessageBox::New(
UIMessageBox::INPUT, i18n( "set_line_breaking_column",
"Set Line Breaking Column:\nSet 0 to disable it.\n" )
.unescape() );
msgBox->setTitle( mWindowTitle );
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->getTextInput()->setAllowOnlyNumbers( true, false );
msgBox->getTextInput()->setText( String::toString( mConfig.doc.lineBreakingColumn ) );
msgBox->showWhenReady();
msgBox->addEventListener( Event::MsgBoxConfirmClick, [&, msgBox]( const Event* ) {
int val;
if ( String::fromString( val, msgBox->getTextInput()->getText() ) && val >= 0 ) {
mConfig.doc.lineBreakingColumn = val;
mEditorSplitter->forEachEditor(
[val]( UICodeEditor* editor ) { editor->setLineBreakingColumn( val ); } );
msgBox->closeWindow();
}
} );
setFocusEditorOnClose( msgBox );
}
} );
mDocMenu->addSeparator();
// **** PROJECT SETTINGS ****
mProjectDocConfig = mConfig.doc;
mProjectMenu = UIPopUpMenu::New();
mProjectMenu
->addCheckBox( i18n( "use_global_settings", "Use Global Settings" ),
mProjectDocConfig.useGlobalSettings )
->setOnShouldCloseCb( shouldCloseCb )
->setId( "use_global_settings" );
mProjectMenu
->addCheckBox(
i18n( "auto_detect_indent_type_and_width", "Auto Detect Indent Type & Width" ),
mConfig.doc.autoDetectIndentType )
->setId( "auto_indent" )
->setEnabled( !mProjectDocConfig.useGlobalSettings );
UIPopUpMenu* tabTypeMenuProject = UIPopUpMenu::New();
tabTypeMenuProject->addRadioButton( i18n( "tabs", "Tabs" ) )
->setActive( !mProjectDocConfig.doc.indentSpaces )
->setId( "tabs" );
tabTypeMenuProject->addRadioButton( i18n( "spaces", "Spaces" ) )
->setActive( mProjectDocConfig.doc.indentSpaces )
->setId( "spaces" );
mProjectMenu
->addSubMenu( i18n( "indentation_type", "Indentation Type" ), nullptr, tabTypeMenuProject )
->setId( "indent_type" )
->setEnabled( !mProjectDocConfig.useGlobalSettings );
tabTypeMenuProject->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
const String& text = event->getNode()->asType<UIMenuRadioButton>()->getId();
mProjectDocConfig.doc.indentSpaces = text != "tabs";
} );
UIPopUpMenu* indentWidthMenuProject = UIPopUpMenu::New();
for ( int w = 2; w <= 12; w++ )
indentWidthMenuProject
->addRadioButton( String::toString( w ), mProjectDocConfig.doc.indentWidth == w )
->setId( String::format( "indent_width_%d", w ) )
->setData( w );
mProjectMenu
->addSubMenu( i18n( "indent_width", "Indent Width" ), nullptr, indentWidthMenuProject )
->setId( "indent_width" )
->setEnabled( !mProjectDocConfig.useGlobalSettings );
indentWidthMenuProject->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
int width = event->getNode()->getData();
mProjectDocConfig.doc.indentWidth = width;
} );
UIPopUpMenu* tabWidthMenuProject = UIPopUpMenu::New();
for ( int w = 2; w <= 12; w++ )
tabWidthMenuProject
->addRadioButton( String::toString( w ), mProjectDocConfig.doc.tabWidth == w )
->setId( String::format( "tab_width_%d", w ) )
->setData( w );
mProjectMenu->addSubMenu( i18n( "tab_width", "Tab Width" ), nullptr, tabWidthMenuProject )
->setId( "tab_width" )
->setEnabled( !mProjectDocConfig.useGlobalSettings );
tabWidthMenuProject->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
int width = event->getNode()->getData();
mProjectDocConfig.doc.tabWidth = width;
} );
UIPopUpMenu* lineEndingsProjectMenu = UIPopUpMenu::New();
lineEndingsProjectMenu
->addRadioButton( "Windows (CR/LF)", mProjectDocConfig.doc.windowsLineEndings )
->setId( "windows" );
lineEndingsProjectMenu->addRadioButton( "Unix (LF)", !mProjectDocConfig.doc.windowsLineEndings )
->setId( "unix" );
mProjectMenu
->addSubMenu( i18n( "line_endings", "Line Endings" ), nullptr, lineEndingsProjectMenu )
->setId( "line_endings" )
->setEnabled( !mProjectDocConfig.useGlobalSettings );
lineEndingsProjectMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
bool winLe = event->getNode()->asType<UIRadioButton>()->getId() == "windows";
mProjectDocConfig.doc.windowsLineEndings = winLe;
} );
mProjectMenu
->addCheckBox( i18n( "trim_trailing_whitespaces", "Trim Trailing Whitespaces" ),
mConfig.doc.trimTrailingWhitespaces )
->setId( "trim_whitespaces" )
->setEnabled( !mProjectDocConfig.useGlobalSettings );
mProjectMenu
->addCheckBox( i18n( "force_new_line_at_end_of_file", "Force New Line at End of File" ),
mConfig.doc.forceNewLineAtEndOfFile )
->setId( "force_nl" )
->setEnabled( !mProjectDocConfig.useGlobalSettings );
mProjectMenu
->addCheckBox( i18n( "write_unicode_bom", "Write Unicode BOM" ),
mConfig.doc.writeUnicodeBOM )
->setId( "write_bom" )
->setEnabled( !mProjectDocConfig.useGlobalSettings );
mProjectMenu->addSeparator();
mProjectMenu->add( i18n( "line_breaking_column", "Line Breaking Column" ) )
->setId( "line_breaking_column" );
mProjectMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
if ( !mEditorSplitter->getCurEditor() ||
event->getNode()->isType( UI_TYPE_MENU_SEPARATOR ) ||
event->getNode()->isType( UI_TYPE_MENUSUBMENU ) )
return;
const String& id = event->getNode()->getId();
if ( event->getNode()->isType( UI_TYPE_MENUCHECKBOX ) ) {
UIMenuCheckBox* item = event->getNode()->asType<UIMenuCheckBox>();
if ( "use_global_settings" == id ) {
mProjectDocConfig.useGlobalSettings = item->isActive();
updateProjectSettingsMenu();
} else if ( "trim_whitespaces" == id ) {
mProjectDocConfig.doc.trimTrailingWhitespaces = item->isActive();
} else if ( "force_nl" == id ) {
mProjectDocConfig.doc.forceNewLineAtEndOfFile = item->isActive();
} else if ( "write_bom" == id ) {
mProjectDocConfig.doc.writeUnicodeBOM = item->isActive();
} else if ( "auto_indent" == id ) {
mProjectDocConfig.doc.autoDetectIndentType = item->isActive();
}
} else if ( "line_breaking_column" == id ) {
UIMessageBox* msgBox = UIMessageBox::New(
UIMessageBox::INPUT, i18n( "set_line_breaking_column",
"Set Line Breaking Column:\nSet 0 to disable it.\n" )
.unescape() );
msgBox->setTitle( mWindowTitle );
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->getTextInput()->setAllowOnlyNumbers( true, false );
msgBox->getTextInput()->setText(
String::toString( mProjectDocConfig.doc.lineBreakingColumn ) );
msgBox->showWhenReady();
msgBox->addEventListener( Event::MsgBoxConfirmClick, [&, msgBox]( const Event* ) {
int val;
if ( String::fromString( val, msgBox->getTextInput()->getText() ) && val >= 0 ) {
mProjectDocConfig.doc.lineBreakingColumn = val;
mEditorSplitter->forEachEditor(
[val]( UICodeEditor* editor ) { editor->setLineBreakingColumn( val ); } );
msgBox->closeWindow();
}
} );
setFocusEditorOnClose( msgBox );
}
} );
mDocMenu
->addSubMenu( i18n( "folder_project_settings", "Folder/Project Settings" ),
findIcon( "folder-user" ), mProjectMenu )
->setId( "project_settings" );
return mDocMenu;
}
void App::updateProjectSettingsMenu() {
mDocMenu->getItemId( "project_settings" )->setEnabled( !mCurrentProject.empty() );
for ( size_t i = 0; i < mProjectMenu->getCount(); i++ ) {
mProjectMenu->getItem( i )->setEnabled( !mCurrentProject.empty() &&
!mProjectDocConfig.useGlobalSettings );
}
mEditorSplitter->forEachEditor( [&]( UICodeEditor* editor ) {
editor->setLineBreakingColumn( !mCurrentProject.empty() &&
!mProjectDocConfig.useGlobalSettings
? mProjectDocConfig.doc.lineBreakingColumn
: mConfig.doc.lineBreakingColumn );
} );
mProjectMenu->getItemId( "trim_whitespaces" )
->asType<UIMenuCheckBox>()
->setActive( mProjectDocConfig.doc.trimTrailingWhitespaces );
mProjectMenu->getItemId( "force_nl" )
->asType<UIMenuCheckBox>()
->setActive( mProjectDocConfig.doc.forceNewLineAtEndOfFile );
mProjectMenu->getItemId( "write_bom" )
->asType<UIMenuCheckBox>()
->setActive( mProjectDocConfig.doc.writeUnicodeBOM );
mProjectMenu->getItemId( "auto_indent" )
->asType<UIMenuCheckBox>()
->setActive( mProjectDocConfig.doc.autoDetectIndentType );
auto* curIndent =
mProjectMenu->find( "indent_width" )
->asType<UIMenuSubMenu>()
->getSubMenu()
->find( String::format( "indent_width_%d", mProjectDocConfig.doc.indentWidth ) );
if ( curIndent )
curIndent->asType<UIMenuRadioButton>()->setActive( true );
mProjectMenu->find( "indent_type" )
->asType<UIMenuSubMenu>()
->getSubMenu()
->find( !mProjectDocConfig.doc.indentSpaces ? "tabs" : "spaces" )
->asType<UIMenuRadioButton>()
->setActive( true );
mProjectMenu->find( "tab_width" )
->asType<UIMenuSubMenu>()
->getSubMenu()
->find( String::format( "tab_width_%d", mProjectDocConfig.doc.tabWidth ) )
->asType<UIMenuRadioButton>()
->setActive( true );
mProjectMenu->find( "line_endings" )
->asType<UIMenuSubMenu>()
->getSubMenu()
->find( mProjectDocConfig.doc.windowsLineEndings ? "windows" : "unix" )
->asType<UIMenuRadioButton>()
->setActive( true );
mProjectMenu->getItemId( "use_global_settings" )
->setEnabled( true )
->asType<UIMenuCheckBox>()
->setActive( mProjectDocConfig.useGlobalSettings );
}
void App::updateDocumentMenu() {
if ( !mEditorSplitter->getCurEditor() )
return;
@@ -1338,7 +1606,7 @@ std::vector<std::string> App::getUnlockedCommands() {
}
void App::closeEditors() {
mConfig.saveProject( mCurrentProject, mEditorSplitter, mConfigPath );
mConfig.saveProject( mCurrentProject, mEditorSplitter, mConfigPath, mProjectDocConfig );
std::vector<UICodeEditor*> editors = mEditorSplitter->getAllEditors();
for ( auto editor : editors ) {
UITabWidget* tabWidget = mEditorSplitter->tabWidgetFromEditor( editor );
@@ -1348,8 +1616,12 @@ void App::closeEditors() {
mDirTree = nullptr;
if ( mFileSystemListener )
mFileSystemListener->setDirTree( mDirTree );
// Force to update the closed tabs.
SceneManager::instance()->update();
mProjectDocConfig = ProjectDocumentConfig( mConfig.doc );
updateProjectSettingsMenu();
}
void App::closeFolder() {
@@ -1488,7 +1760,9 @@ NotificationCenter* App::getNotificationCenter() const {
void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) {
const CodeEditorConfig& config = mConfig.editor;
const DocumentConfig& docc = mConfig.doc;
const DocumentConfig& docc = !mCurrentProject.empty() && !mProjectDocConfig.useGlobalSettings
? mProjectDocConfig.doc
: mConfig.doc;
editor->setFontSize( config.fontSize.asDp( 0, Sizef(), mUISceneNode->getDPI() ) );
editor->setEnableColorPickerOnSelection( true );
editor->setColorScheme( mEditorSplitter->getCurrentColorScheme() );
@@ -1895,8 +2169,8 @@ UIMenu* App::createColorSchemeMenu() {
const auto& colorSchemes = mEditorSplitter->getColorSchemes();
for ( auto& colorScheme : colorSchemes ) {
menu->addRadioButton(
colorScheme.first, mEditorSplitter->getCurrentColorSchemeName() == colorScheme.first );
menu->addRadioButton( colorScheme.first,
mEditorSplitter->getCurrentColorSchemeName() == colorScheme.first );
if ( mColorSchemeMenues.size() == 1 && menu->getCount() == 1 ) {
menu->reloadStyle( true, true );
@@ -2294,7 +2568,7 @@ void App::loadFolder( const std::string& path ) {
mCurrentProject = rpath;
loadDirTree( rpath );
mConfig.loadProject( rpath, mEditorSplitter, mConfigPath, mThreadPool );
mConfig.loadProject( rpath, mEditorSplitter, mConfigPath, mProjectDocConfig, mThreadPool );
mFileSystemModel = FileSystemModel::New( rpath, FileSystemModel::Mode::FilesAndDirectories,
{ true, true, true } );
@@ -2312,6 +2586,7 @@ void App::loadFolder( const std::string& path ) {
mRecentFolders.resize( 10 );
updateRecentFolders();
updateProjectSettingsMenu();
if ( mEditorSplitter->getCurEditor() )
mEditorSplitter->getCurEditor()->setFocus();
@@ -2677,7 +2952,8 @@ void App::init( std::string file, const Float& pidelDensity, const std::string&
{ "layout-left", 0xee94 },
{ "layout-right", 0xee9b },
{ "color-scheme", 0xebd4 },
{ "global_settings", 0xedcf },
{ "global-settings", 0xedcf },
{ "folder-user", 0xed84 },
};
for ( const auto& icon : icons )
iconTheme->add( UIGlyphIcon::New( icon.first, iconFont, icon.second ) );

View File

@@ -97,6 +97,7 @@ class App : public UICodeEditorSplitter::Client {
UIPopUpMenu* mWindowMenu{ nullptr };
UIPopUpMenu* mToolsMenu{ nullptr };
UIPopUpMenu* mProjectTreeMenu{ nullptr };
UIPopUpMenu* mProjectMenu{ nullptr };
UISplitter* mProjectSplitter{ nullptr };
UITabWidget* mSidePanel{ nullptr };
UICodeEditorSplitter* mEditorSplitter{ nullptr };
@@ -116,6 +117,7 @@ class App : public UICodeEditorSplitter::Client {
std::shared_ptr<FileSystemModel> mFileSystemModel;
size_t mMenuIconSize;
bool mDirTreeReady{ false };
ProjectDocumentConfig mProjectDocConfig;
std::unordered_set<Doc::TextDocument*> mTmpDocs;
std::string mCurrentProject;
FontTrueType* mFont{ nullptr };
@@ -188,7 +190,9 @@ class App : public UICodeEditorSplitter::Client {
Drawable* findIcon( const std::string& name );
String i18n( const std::string& name, const String& def );
String i18n( const std::string& key, const String& def );
void updateProjectSettingsMenu();
UIMenu* createDocumentMenu();