From bd9170baee99ae8eb048f3e2bedff9c3977d36fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 7 Jun 2020 19:52:39 -0300 Subject: [PATCH] Added color picking capability in the UICodeEditor. Some minor improvements to the code editor. Some minor fixes. --- bin/assets/colorschemes/colorschemes.conf | 2 +- bin/assets/ui/breeze.css | 2 +- include/eepp/core/string.hpp | 3 + include/eepp/system/color.hpp | 4 + include/eepp/ui/uicodeeditor.hpp | 7 ++ src/eepp/core/string.cpp | 8 ++ src/eepp/system/color.cpp | 13 ++- src/eepp/ui/tools/uicolorpicker.cpp | 6 +- src/eepp/ui/uicodeeditor.cpp | 54 ++++++++++++- src/eepp/ui/uimenu.cpp | 4 +- src/eepp/ui/uimenusubmenu.cpp | 13 +-- src/eepp/ui/uitextview.cpp | 1 + src/tools/codeeditor/codeeditor.cpp | 97 +++++++++++++++++++++-- src/tools/codeeditor/codeeditor.hpp | 7 ++ 14 files changed, 201 insertions(+), 20 deletions(-) diff --git a/bin/assets/colorschemes/colorschemes.conf b/bin/assets/colorschemes/colorschemes.conf index ba74bfd16..744a4aca5 100644 --- a/bin/assets/colorschemes/colorschemes.conf +++ b/bin/assets/colorschemes/colorschemes.conf @@ -86,7 +86,7 @@ string = #f1fa8c operator = #ff79c6 function = #50fa7b -[gruvbox_dark] +[gruvbox dark] background = #282828 text = #928374 caret = #fbf1c7 diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 97b77d5c4..65bded2f0 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -654,7 +654,7 @@ Menu::Separator { Menu::SubMenu::Arrow { width: 16dp; height: 16dp; - foreground-image: poly(line, var(--icon), "-7dp -5dp, -2dp 0dp"), poly(line, var(--icon), "-7dp 5dp, -2dp 0dp"); + foreground-image: poly(line, var(--icon), "5dp 3dp, 10dp 8dp"), poly(line, var(--icon), "5dp 13dp, 10dp 8dp"); } Menu::Item::icon, diff --git a/include/eepp/core/string.hpp b/include/eepp/core/string.hpp index 7b53110b8..75f6c446d 100644 --- a/include/eepp/core/string.hpp +++ b/include/eepp/core/string.hpp @@ -94,6 +94,9 @@ class EE_API String { /** @return If the value passed is a letter */ static bool isLetter( const int& value ); + /** @return If the string is a representation of a hexa number */ + static bool isHexNotation( const std::string& value, const std::string& withPrefix = "" ); + /** Split a String and hold it on a vector */ static std::vector split( const String& str, const Uint32& splitchar = '\n', const bool& pushEmptyString = false ); diff --git a/include/eepp/system/color.hpp b/include/eepp/system/color.hpp index f06619cb8..4147fed33 100644 --- a/include/eepp/system/color.hpp +++ b/include/eepp/system/color.hpp @@ -160,6 +160,10 @@ class EE_API Color : public tColor { std::string toHexString( const bool& prependHash = true ) const; + std::string toRgbaString() const; + + std::string toRgbString() const; + Color& blendAlpha( const Uint8& alpha ); static Color fromHsl( const Colorf& hsl ); diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 5dc9fca0f..db21791c8 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -207,6 +207,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void setSelectionMatchColor( const Color& highlightSelectionMatchColor ); + const bool& getEnableColorPickerOnSelection() const; + + void setEnableColorPickerOnSelection( const bool& enableColorPickerOnSelection ); + protected: struct LastXOffset { TextPosition position; @@ -226,6 +230,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool mHighlightCurrentLine; bool mHighlightMatchingBracket; bool mHighlightSelectionMatch; + bool mEnableColorPickerOnSelection; Uint32 mTabWidth; Int64 mLastColOffset; Vector2f mScroll; @@ -362,6 +367,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void fontSizeReset(); + void checkColorPickerAction(); + virtual void drawMatchingBrackets( const Vector2f& startScroll, const Float& lineHeight ); virtual void drawSelectionMatch( const std::pair& lineRange, diff --git a/src/eepp/core/string.cpp b/src/eepp/core/string.cpp index a6de8e376..ad0f3474b 100644 --- a/src/eepp/core/string.cpp +++ b/src/eepp/core/string.cpp @@ -59,6 +59,14 @@ bool String::isLetter( const int& value ) { ( value != 215 ) && ( value != 247 ) ); } +bool String::isHexNotation( const std::string& value, const std::string& withPrefix ) { + if ( !withPrefix.empty() && !String::startsWith( value, withPrefix ) ) { + return false; + } + return value.find_first_not_of( "0123456789abcdefABCDEF", withPrefix.size() ) == + std::string::npos; +} + std::vector String::split( const String& str, const Uint32& splitchar, const bool& pushEmptyString ) { std::vector tmp; diff --git a/src/eepp/system/color.cpp b/src/eepp/system/color.cpp index 96f19a416..a33af2647 100644 --- a/src/eepp/system/color.cpp +++ b/src/eepp/system/color.cpp @@ -229,7 +229,18 @@ std::string Color::toHexString( const bool& prependHashtag ) const { if ( prependHashtag ) stream << "#"; stream << std::setfill( '0' ) << std::setw( sizeof( Color ) * 2 ) << std::hex << getValue(); - return stream.str(); + std::string str = stream.str(); + if ( this->a == 255 ) + return str.substr( 0, 6 ); + return str; +} + +std::string Color::toRgbaString() const { + return String::format( "rgba(%d, %d, %d, %.2f)", r, g, b, a / 255.f ); +} + +std::string Color::toRgbString() const { + return String::format( "rgb(%d, %d, %d)", r, g, b ); } Color& Color::blendAlpha( const Uint8& alpha ) { diff --git a/src/eepp/ui/tools/uicolorpicker.cpp b/src/eepp/ui/tools/uicolorpicker.cpp index 2f0f10f61..86c826bce 100644 --- a/src/eepp/ui/tools/uicolorpicker.cpp +++ b/src/eepp/ui/tools/uicolorpicker.cpp @@ -257,7 +257,7 @@ UIColorPicker::UIColorPicker( UIWindow* attachTo, const UIColorPicker::ColorPick mRoot->getUISceneNode()->getUIThemeManager()->getControlsFadeOutTime() ) ); } mUIWindow->getModalControl()->addEventListener( - Event::MouseClick, [&]( const Event* event ) { + Event::MouseClick, [&]( const Event* ) { if ( mModalAlpha != 0.f ) mUIWindow->getModalControl()->runAction( Actions::FadeOut::New( mRoot->getUISceneNode()->getUIThemeManager()->getControlsFadeOutTime() ) ); @@ -277,9 +277,9 @@ UIColorPicker::UIColorPicker( UIWindow* attachTo, const UIColorPicker::ColorPick mRoot->bind( "alpha_container", mAlphaContainer ); mRoot->bind( "footer", mFooter ); - mRoot->addEventListener( Event::OnSizeChange, [&]( const Event* event ) { updateAll(); } ); + mRoot->addEventListener( Event::OnSizeChange, [&]( const Event* ) { updateAll(); } ); - mRoot->addEventListener( Event::OnLayoutUpdate, [&]( const Event* event ) { + mRoot->addEventListener( Event::OnLayoutUpdate, [&]( const Event* ) { if ( mHuePicker->getDrawable() == nullptr ) { mHuePicker->setDrawable( createHueTexture( mHuePicker->getPixelsSize() ), true ); mCurrentColor->setBackgroundDrawable( createGridTexture(), true ); diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 7b8423c9f..91a2598bc 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,8 @@ #include #include +using namespace EE::UI::Tools; + namespace EE { namespace UI { UICodeEditor* UICodeEditor::New() { @@ -31,6 +34,7 @@ UICodeEditor::UICodeEditor() : mHighlightCurrentLine( true ), mHighlightMatchingBracket( true ), mHighlightSelectionMatch( true ), + mEnableColorPickerOnSelection( false ), mTabWidth( 4 ), mLastColOffset( 0 ), mMouseWheelScroll( 50 ), @@ -541,6 +545,45 @@ Uint32 UICodeEditor::onMouseClick( const Vector2i&, const Uint32& flags ) { return 1; } +void UICodeEditor::checkColorPickerAction() { + if ( !mEnableColorPickerOnSelection ) + return; + String text( mDoc.getSelectedText() ); + TextRange range( mDoc.getSelection( true ) ); + if ( range.start().line() != range.end().line() ) + return; + const String& line = mDoc.line( range.end().line() ).getText(); + bool isHash = range.start().column() > 0 && + mDoc.line( range.start().line() ).getText()[range.start().column() - 1] == '#' && + ( text.size() == 6 || text.size() == 8 ) && String::isHexNotation( text ); + bool isRgba = !isHash && text == "rgba" && range.end().column() < (Int64)line.size() - 1 && + line[range.end().column()] == '('; + bool isRgb = !isHash && !isRgba && text == "rgb" && + range.end().column() < (Int64)line.size() - 1 && line[range.end().column()] == '('; + if ( isHash || isRgb || isRgba ) { + UIColorPicker* colorPicker = NULL; + if ( isHash ) { + colorPicker = UIColorPicker::NewModal( + this, [&]( Color color ) { mDoc.replaceSelection( color.toHexString( false ) ); } ); + colorPicker->setColor( Color( '#' + text ) ); + } else if ( isRgba || isRgb ) { + TextPosition position = mDoc.findCloseBracket( + {range.start().line(), static_cast( range.end().column() )}, '(', ')' ); + if ( position.isValid() ) { + mDoc.setSelection( {position.line(), position.column() + 1}, range.start() ); + colorPicker = UIColorPicker::NewModal( this, [&, isRgba]( Color color ) { + mDoc.replaceSelection( isRgba || color.a != 255 ? color.toRgbaString() + : color.toRgbString() ); + } ); + colorPicker->setColor( Color::fromString( mDoc.getSelectedText() ) ); + } + } + if ( colorPicker ) + colorPicker->getUIWindow()->addEventListener( Event::OnWindowClose, + [&]( const Event* ) { setFocus(); } ); + } +} + Uint32 UICodeEditor::onMouseDoubleClick( const Vector2i&, const Uint32& flags ) { if ( mLocked || NULL == mFont ) return 1; @@ -548,6 +591,7 @@ Uint32 UICodeEditor::onMouseDoubleClick( const Vector2i&, const Uint32& flags ) if ( flags & EE_BUTTON_LMASK ) { mDoc.selectWord(); mLastDoubleClick.restart(); + checkColorPickerAction(); } return 1; } @@ -1004,6 +1048,14 @@ void UICodeEditor::setSelectionMatchColor( const Color& highlightSelectionMatchC } } +const bool& UICodeEditor::getEnableColorPickerOnSelection() const { + return mEnableColorPickerOnSelection; +} + +void UICodeEditor::setEnableColorPickerOnSelection( const bool& enableColorPickerOnSelection ) { + mEnableColorPickerOnSelection = enableColorPickerOnSelection; +} + void UICodeEditor::checkMatchingBrackets() { if ( mHighlightMatchingBracket ) { mMatchingBrackets = TextRange(); @@ -1375,7 +1427,7 @@ void UICodeEditor::registerKeybindings() { {{KEY_UP, 0}, "move-to-previous-line"}, {{KEY_DOWN, KEYMOD_CTRL | KEYMOD_SHIFT}, "move-lines-down"}, {{KEY_DOWN, KEYMOD_CTRL}, "move-scroll-down"}, - {{KEY_DOWN, KEYMOD_SHIFT}, "select-to-next-line"}, + {{KEY_DOWN, KEYMOD_SHIFT}, "select-to-next-line"}, {{KEY_DOWN, 0}, "move-to-next-line"}, {{KEY_LEFT, KEYMOD_CTRL | KEYMOD_SHIFT}, "select-to-previous-word"}, {{KEY_LEFT, KEYMOD_CTRL}, "move-to-previous-word"}, diff --git a/src/eepp/ui/uimenu.cpp b/src/eepp/ui/uimenu.cpp index c9c9b8d7d..59e355619 100644 --- a/src/eepp/ui/uimenu.cpp +++ b/src/eepp/ui/uimenu.cpp @@ -151,7 +151,7 @@ Uint32 UIMenu::add( UIWidget* widget ) { mItems.push_back( widget ); - widget->addEventListener( Event::OnSizeChange, [&]( const Event* event ) { + widget->addEventListener( Event::OnSizeChange, [&]( const Event* ) { if ( !mResizing ) { widgetsSetPos(); widgetsResize(); @@ -182,7 +182,7 @@ Uint32 UIMenu::addSeparator() { resizeMe(); - separator->addEventListener( Event::OnSizeChange, [&]( const Event* event ) { + separator->addEventListener( Event::OnSizeChange, [&]( const Event* ) { if ( !mResizing ) { widgetsSetPos(); widgetsResize(); diff --git a/src/eepp/ui/uimenusubmenu.cpp b/src/eepp/ui/uimenusubmenu.cpp index 1dc6761fa..7a0e8dbd2 100644 --- a/src/eepp/ui/uimenusubmenu.cpp +++ b/src/eepp/ui/uimenusubmenu.cpp @@ -19,12 +19,11 @@ UIMenuSubMenu::UIMenuSubMenu() : mArrow = UIWidget::NewWithTag( getElementTag() + "::arrow" ); mArrow->setParent( this ); mArrow->setFlags( UI_AUTO_SIZE ); + applyDefaultTheme(); + mArrow->addEventListener( Event::OnSizeChange, [&]( const Event* ) { onSizeChange(); } ); + mArrow->addEventListener( Event::OnMarginChange, [&]( const Event* ) { onSizeChange(); } ); mArrow->setVisible( true ); mArrow->setEnabled( false ); - mArrow->addEventListener( Event::OnMarginChange, - [&]( const Event* event ) { onSizeChange(); } ); - - applyDefaultTheme(); } UIMenuSubMenu::~UIMenuSubMenu() {} @@ -41,7 +40,11 @@ void UIMenuSubMenu::setTheme( UITheme* Theme ) { UIMenuItem::setTheme( Theme ); mArrow->setThemeSkin( "menuarrow" ); - mArrow->setSize( mArrow->getSkinSize() ); + + Sizef skinSize( mArrow->getSkinSize() ); + if ( skinSize != Sizef::Zero ) { + mArrow->setSize( skinSize ); + } onStateChange(); diff --git a/src/eepp/ui/uitextview.cpp b/src/eepp/ui/uitextview.cpp index 31f56ae7e..1fcda661b 100644 --- a/src/eepp/ui/uitextview.cpp +++ b/src/eepp/ui/uitextview.cpp @@ -211,6 +211,7 @@ UITextView* UITextView::setFontColor( const Color& color ) { mFontStyleConfig.FontColor = color; Color newColor( color.r, color.g, color.b, color.a * mAlpha / 255.f ); mTextCache->setFillColor( newColor ); + invalidateDraw(); } return this; diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index ee59bf194..864d67cf4 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -183,6 +183,7 @@ void App::applyColorScheme( const SyntaxColorScheme& colorScheme ) { UICodeEditor* App::createCodeEditor() { UICodeEditor* codeEditor = UICodeEditor::New(); codeEditor->setFontSize( 11 ); + codeEditor->setEnableColorPickerOnSelection( true ); codeEditor->setColorScheme( mColorSchemes[mCurrentColorScheme] ); codeEditor->getDocument().setCommand( "switch-to-previous-colorscheme", [&] { auto it = mColorSchemes.find( mCurrentColorScheme ); @@ -477,6 +478,7 @@ void App::setAppTitle( const std::string& title ) { void App::loadFileFromPath( const std::string& path, UICodeEditor* codeEditor ) { if ( NULL == codeEditor ) codeEditor = mCurEditor; + codeEditor->setColorScheme( mColorSchemes[mCurrentColorScheme] ); codeEditor->loadFromFile( path ); updateEditorTitle( codeEditor ); } @@ -501,7 +503,7 @@ void App::openFileDialog() { } } ); TGDialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) { - if ( mCurEditor ) + if ( mCurEditor && !SceneManager::instance()->isShootingDown() ) mCurEditor->setFocus(); } ); TGDialog->center(); @@ -586,6 +588,12 @@ void App::findAndReplace( String find, String replace, const bool& caseSensitive } } +void App::runCommand( const std::string& command ) { + if ( mCurEditor ) { + mCurEditor->getDocument().execute( command ); + } +} + void App::initSearchBar() { auto addClickListener = [&]( UIWidget* widget, std::string cmd ) { widget->addEventListener( Event::MouseClick, [this, cmd]( const Event* event ) { @@ -731,6 +739,64 @@ App::~App() { eeSAFE_DELETE( mConsole ); } +void App::createSettingsMenu() { + mSettingsMenu = UIPopUpMenu::New(); + mSettingsMenu->add( "New" ); + mSettingsMenu->add( "Open..." ); + mSettingsMenu->addSeparator(); + mSettingsMenu->add( "Save" ); + mSettingsMenu->add( "Save as..." ); + mSettingsMenu->addSeparator(); + mSettingsMenu->addSubMenu( "Color Scheme", NULL, createColorSchemeMenu() ); + mSettingsMenu->addSeparator(); + mSettingsMenu->add( "Close" ); + mSettingsMenu->addSeparator(); + mSettingsMenu->add( "Quit" ); + mSettingsButton = mUISceneNode->find( "settings" ); + mSettingsButton->addEventListener( Event::MouseClick, [&]( const Event* ) { + Vector2f pos( mSettingsButton->getPixelsPosition() ); + mSettingsButton->nodeToWorldTranslation( pos ); + UIMenu::fixMenuPos( pos, mSettingsMenu ); + mSettingsMenu->setPixelsPosition( pos ); + mSettingsMenu->show(); + } ); + mSettingsMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) { + const String& name = event->getNode()->asType()->getText(); + if ( name == "New" ) { + runCommand( "create-new" ); + } else if ( name == "Open..." ) { + runCommand( "open-file" ); + } else if ( name == "Save" ) { + runCommand( "save-doc" ); + } else if ( name == "Save as..." ) { + + } else if ( name == "Close" ) { + runCommand( "close-doc" ); + } else if ( name == "Quit" ) { + runCommand( "close-app" ); + } + } ); +} + +UIMenu* App::createColorSchemeMenu() { + UIPopUpMenu* colorSchemeMenu = UIPopUpMenu::New(); + for ( auto& colorScheme : mColorSchemes ) { + colorSchemeMenu->addCheckBox( colorScheme.first, mCurrentColorScheme == colorScheme.first ); + } + colorSchemeMenu->addEventListener( + Event::OnItemClicked, [&, colorSchemeMenu]( const Event* event ) { + UIMenuItem* item = event->getNode()->asType(); + const String& name = item->getText(); + mCurrentColorScheme = name; + applyColorScheme( mColorSchemes[mCurrentColorScheme] ); + for ( size_t i = 0; i < colorSchemeMenu->getCount(); i++ ) { + UIMenuCheckBox* menuItem = colorSchemeMenu->getItem( i )->asType(); + menuItem->setActive( mCurrentColorScheme == menuItem->getText() ); + } + } ); + return colorSchemeMenu; +} + void App::init( const std::string& file ) { DisplayManager* displayManager = Engine::instance()->getDisplayManager(); Display* currentDisplay = displayManager->getDisplayIndex( 0 ); @@ -769,14 +835,18 @@ void App::init( const std::string& file ) { Font* fontMono = FontTrueType::New( "monospace", resPath + "assets/fonts/DejaVuSansMono.ttf" ); - mUISceneNode->getUIThemeManager()->setDefaultFont( font ); + FontTrueType::New( "icon", resPath + "assets/fonts/remixicon.ttf" ); SceneManager::instance()->add( mUISceneNode ); - StyleSheetParser cssParser; - if ( cssParser.loadFromFile( resPath + "assets/ui/breeze.css" ) ) { - mUISceneNode->setStyleSheet( cssParser.getStyleSheet() ); - } + UITheme* theme = + UITheme::load( "uitheme", "uitheme", "", font, resPath + "assets/ui/breeze.css" ); + mUISceneNode->setStyleSheet( theme->getStyleSheet() ); + mUISceneNode->getUIThemeManager() + ->setDefaultEffectsEnabled( true ) + ->setDefaultTheme( theme ) + ->setDefaultFont( font ) + ->add( theme ); auto colorSchemes = SyntaxColorScheme::loadFromFile( resPath + "assets/colorschemes/colorschemes.conf" ); @@ -815,7 +885,19 @@ void App::init( const std::string& file ) { .close_button:hover { background-color: var(--icon-back-alert); } + #settings { + color: #eff0f188; + font-family: icon; + font-size: 16dp; + margin-top: 2dp; + margin-right: 22dp; + transition: all 0.15s; + } + #settings:hover { + color: var(--primary); + } + @@ -845,6 +927,8 @@ void App::init( const std::string& file ) { + + )xml"; UIWidgetCreator::registerWidget( "searchbar", [] { return UISearchBar::New(); } ); @@ -854,6 +938,7 @@ void App::init( const std::string& file ) { mSearchBarLayout->setVisible( false )->setEnabled( false ); initSearchBar(); + createSettingsMenu(); createEditorWithTabWidget( mBaseLayout ); if ( !file.empty() ) { diff --git a/src/tools/codeeditor/codeeditor.hpp b/src/tools/codeeditor/codeeditor.hpp index f4b7ff3a2..9b075d610 100644 --- a/src/tools/codeeditor/codeeditor.hpp +++ b/src/tools/codeeditor/codeeditor.hpp @@ -91,6 +91,7 @@ class App { void findAndReplace( String find, String replace, const bool& caseSensitive ); + void runCommand( const std::string& command ); protected: EE::Window::Window* mWindow{NULL}; UISceneNode* mUISceneNode{NULL}; @@ -104,6 +105,8 @@ class App { std::vector mTabWidgets; std::map mColorSchemes; std::string mCurrentColorScheme; + UIPopUpMenu* mSettingsMenu; + UITextView* mSettingsButton; void onFileDropped( String file ); @@ -128,6 +131,10 @@ class App { void initSearchBar(); void addRemainingTabWidgets( Node* widget ); + + void createSettingsMenu(); + + UIMenu* createColorSchemeMenu(); }; #endif // EE_TOOLS_CODEEDITOR_HPP