mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-09-22 13:01:05 +03:00
Improve tag creation and add UITextEdit hint support
Add native hint text support to UITextEdit, matching UITextInput behavior and exposing configurable hint text, color, font, size, style, shadow, outline, and HintDisplay modes. Render hints only for empty documents and preserve the correct drawing order beneath the caret. Expose the corresponding hint CSS properties through UITextEdit and apply the theme hint color to TextEdit and TextArea widgets. Add an “Add Tag & Push” action alongside local-only tag creation. Push only the newly created tag using an explicit refspec and the current branch’s tracked remote, falling back to origin.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include <eepp/graphics/font.hpp>
|
||||
#include <eepp/graphics/primitives.hpp>
|
||||
#include <eepp/graphics/resourcescope.hpp>
|
||||
#include <eepp/graphics/text.hpp>
|
||||
#include <eepp/system/log.hpp>
|
||||
#include <eepp/ui/css/propertydefinition.hpp>
|
||||
@@ -45,6 +46,11 @@ UITextEdit::UITextEdit( const std::string& tag ) : UICodeEditor( tag, true, true
|
||||
}
|
||||
}
|
||||
|
||||
mHintCache.setTextHints( getWidgetTextDrawHints() );
|
||||
setHintFont( theme && theme->getDefaultFont() ? theme->getDefaultFont() : getFont() );
|
||||
setHintFontSize( theme ? theme->getDefaultFontSize()
|
||||
: getUISceneNode()->getUIThemeManager()->getDefaultFontSize() );
|
||||
|
||||
disableEditorFeatures();
|
||||
applyDefaultTheme();
|
||||
}
|
||||
@@ -90,6 +96,135 @@ void UITextEdit::setWordWrap( bool enabled ) {
|
||||
setLineWrapMode( enabled ? LineWrapMode::Word : LineWrapMode::NoWrap );
|
||||
}
|
||||
|
||||
const String& UITextEdit::getHint() const {
|
||||
return mHintCache.getString();
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHint( const String& hint ) {
|
||||
if ( hint != mHintCache.getString() ) {
|
||||
mHintCache.setString( hint );
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
const Color& UITextEdit::getHintColor() const {
|
||||
return mHintStyleConfig.getFontColor();
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHintColor( const Color& hintColor ) {
|
||||
if ( hintColor != mHintStyleConfig.getFontColor() ) {
|
||||
mHintCache.setFillColor( hintColor );
|
||||
mHintStyleConfig.FontColor = hintColor;
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
const Color& UITextEdit::getHintShadowColor() const {
|
||||
return mHintStyleConfig.getFontShadowColor();
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHintShadowColor( const Color& shadowColor ) {
|
||||
if ( shadowColor != mHintStyleConfig.getFontShadowColor() ) {
|
||||
mHintCache.setShadowColor( shadowColor );
|
||||
mHintStyleConfig.ShadowColor = shadowColor;
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
const Vector2f& UITextEdit::getHintShadowOffset() const {
|
||||
return mHintStyleConfig.getFontShadowOffset();
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHintShadowOffset( const Vector2f& shadowOffset ) {
|
||||
if ( shadowOffset != mHintStyleConfig.getFontShadowOffset() ) {
|
||||
mHintCache.setShadowOffset( shadowOffset );
|
||||
mHintStyleConfig.ShadowOffset = shadowOffset;
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Font* UITextEdit::getHintFont() const {
|
||||
return mHintStyleConfig.getFont();
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHintFont( Font* font ) {
|
||||
if ( font != mHintStyleConfig.getFont() ) {
|
||||
mHintCache.setFont( font );
|
||||
mHintStyleConfig.Font = font;
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Uint32 UITextEdit::getHintFontSize() const {
|
||||
return mHintCache.getCharacterSize();
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHintFontSize( const Uint32& characterSize ) {
|
||||
if ( characterSize != mHintCache.getCharacterSize() ) {
|
||||
mHintCache.setFontSize( characterSize );
|
||||
mHintStyleConfig.CharacterSize = characterSize;
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
const Uint32& UITextEdit::getHintFontStyle() const {
|
||||
return mHintStyleConfig.Style;
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHintFontStyle( const Uint32& fontStyle ) {
|
||||
if ( fontStyle != mHintStyleConfig.Style ) {
|
||||
mHintCache.setStyle( fontStyle );
|
||||
mHintStyleConfig.Style = fontStyle;
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
const Float& UITextEdit::getHintOutlineThickness() const {
|
||||
return mHintStyleConfig.OutlineThickness;
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHintOutlineThickness( const Float& outlineThickness ) {
|
||||
if ( outlineThickness != mHintStyleConfig.OutlineThickness ) {
|
||||
mHintCache.setOutlineThickness( outlineThickness );
|
||||
mHintStyleConfig.OutlineThickness = outlineThickness;
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
const Color& UITextEdit::getHintOutlineColor() const {
|
||||
return mHintStyleConfig.OutlineColor;
|
||||
}
|
||||
|
||||
UITextEdit* UITextEdit::setHintOutlineColor( const Color& outlineColor ) {
|
||||
if ( outlineColor != mHintStyleConfig.OutlineColor ) {
|
||||
mHintStyleConfig.OutlineColor = outlineColor;
|
||||
Color color( outlineColor.r, outlineColor.g, outlineColor.b,
|
||||
outlineColor.a * mAlpha / 255.f );
|
||||
mHintCache.setOutlineColor( color );
|
||||
invalidateDraw();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void UITextEdit::setHintDisplay( HintDisplay display ) {
|
||||
if ( display != mHintDisplay ) {
|
||||
mHintDisplay = display;
|
||||
invalidateDraw();
|
||||
}
|
||||
}
|
||||
|
||||
HintDisplay UITextEdit::getHintDisplay() const {
|
||||
return mHintDisplay;
|
||||
}
|
||||
|
||||
bool UITextEdit::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
if ( !checkPropertyDefinition( attribute ) )
|
||||
return false;
|
||||
@@ -101,6 +236,42 @@ bool UITextEdit::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
break;
|
||||
case PropertyId::Wordwrap:
|
||||
setWordWrap( attribute.asBool() );
|
||||
break;
|
||||
case PropertyId::Hint:
|
||||
setHint( getTranslatorString( attribute.value() ) );
|
||||
break;
|
||||
case PropertyId::HintColor:
|
||||
setHintColor( attribute.asColor() );
|
||||
break;
|
||||
case PropertyId::HintShadowColor:
|
||||
setHintShadowColor( attribute.asColor() );
|
||||
break;
|
||||
case PropertyId::HintShadowOffset:
|
||||
setHintShadowOffset( attribute.asVector2f() );
|
||||
break;
|
||||
case PropertyId::HintFontSize:
|
||||
setHintFontSize( lengthFromValue( attribute ) );
|
||||
break;
|
||||
case PropertyId::HintFontFamily:
|
||||
setHintFont(
|
||||
getUISceneNode()
|
||||
? getUISceneNode()->getResourceScope()->findFont( attribute.value() ).get()
|
||||
: nullptr );
|
||||
break;
|
||||
case PropertyId::HintFontStyle:
|
||||
setHintFontStyle( attribute.asFontStyle() );
|
||||
break;
|
||||
case PropertyId::HintStrokeWidth:
|
||||
setHintOutlineThickness( PixelDensity::dpToPx( attribute.asDpDimension() ) );
|
||||
break;
|
||||
case PropertyId::HintStrokeColor:
|
||||
setHintOutlineColor( attribute.asColor() );
|
||||
break;
|
||||
case PropertyId::HintDisplay:
|
||||
setHintDisplay( String::toLower( attribute.asString() ) == "focus"
|
||||
? HintDisplay::Focus
|
||||
: HintDisplay::Always );
|
||||
break;
|
||||
default:
|
||||
return UICodeEditor::applyProperty( attribute );
|
||||
}
|
||||
@@ -108,6 +279,65 @@ bool UITextEdit::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string UITextEdit::getPropertyString( const PropertyDefinition* propertyDef,
|
||||
const Uint32& propertyIndex ) const {
|
||||
if ( NULL == propertyDef )
|
||||
return "";
|
||||
|
||||
switch ( propertyDef->getPropertyId() ) {
|
||||
case PropertyId::Hint:
|
||||
return getHint().toUtf8();
|
||||
case PropertyId::HintColor:
|
||||
return getHintColor().toHexString();
|
||||
case PropertyId::HintShadowColor:
|
||||
return getHintShadowColor().toHexString();
|
||||
case PropertyId::HintShadowOffset:
|
||||
return String::fromFloat( getHintShadowOffset().x ) + " " +
|
||||
String::fromFloat( getHintShadowOffset().y );
|
||||
case PropertyId::HintFontSize:
|
||||
return String::format( "%ddp", getHintFontSize() );
|
||||
case PropertyId::HintFontFamily:
|
||||
return getHintFont() ? getUISceneNode()->getFontFamilyName( getHintFont() ) : "";
|
||||
case PropertyId::HintFontStyle:
|
||||
return Text::styleFlagToString( getHintFontStyle() );
|
||||
case PropertyId::HintStrokeWidth:
|
||||
return String::fromFloat( PixelDensity::dpToPx( getHintOutlineThickness() ), "px" );
|
||||
case PropertyId::HintStrokeColor:
|
||||
return getHintOutlineColor().toHexString();
|
||||
case PropertyId::HintDisplay:
|
||||
return mHintDisplay == HintDisplay::Always ? "always" : "focus";
|
||||
default:
|
||||
return UICodeEditor::getPropertyString( propertyDef, propertyIndex );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PropertyId> UITextEdit::getPropertiesImplemented() const {
|
||||
auto props = UICodeEditor::getPropertiesImplemented();
|
||||
auto local = { PropertyId::Hint,
|
||||
PropertyId::HintColor,
|
||||
PropertyId::HintShadowColor,
|
||||
PropertyId::HintShadowOffset,
|
||||
PropertyId::HintFontSize,
|
||||
PropertyId::HintFontFamily,
|
||||
PropertyId::HintFontStyle,
|
||||
PropertyId::HintStrokeWidth,
|
||||
PropertyId::HintStrokeColor,
|
||||
PropertyId::HintDisplay };
|
||||
props.insert( props.end(), local.begin(), local.end() );
|
||||
return props;
|
||||
}
|
||||
|
||||
void UITextEdit::drawLineText( const Int64& line, Vector2f position, const Float& fontSize,
|
||||
const Float& lineHeight,
|
||||
const DocumentViewLineRange& visibleLineRange ) {
|
||||
UICodeEditor::drawLineText( line, position, fontSize, lineHeight, visibleLineRange );
|
||||
if ( line == 0 && mDoc->isEmpty() && !mHintCache.getString().empty() &&
|
||||
( mHintDisplay == HintDisplay::Always || hasFocus() ) ) {
|
||||
mHintCache.draw( std::trunc( position.x ), std::trunc( position.y ), Vector2f::One, 0.f,
|
||||
getBlendMode() );
|
||||
}
|
||||
}
|
||||
|
||||
void UITextEdit::drawCursor( const Vector2f& startScroll, const Float& lineHeight,
|
||||
const TextPosition& cursor ) {
|
||||
if ( mCursorVisible && !mLocked && isTextSelectionEnabled() ) {
|
||||
@@ -121,4 +351,9 @@ void UITextEdit::drawCursor( const Vector2f& startScroll, const Float& lineHeigh
|
||||
}
|
||||
}
|
||||
|
||||
void UITextEdit::onTextHintsChanged() {
|
||||
UICodeEditor::onTextHintsChanged();
|
||||
mHintCache.setTextHints( getWidgetTextDrawHints() );
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -950,6 +950,14 @@ Git::Result Git::createTag( const std::string& name, const std::string& revision
|
||||
return result;
|
||||
}
|
||||
|
||||
Git::Result Git::pushTag( const std::string& name, const std::string& remote,
|
||||
const std::string& projectDir ) {
|
||||
Result result;
|
||||
result.returnCode = git( { "push", remote, "refs/tags/" + name + ":refs/tags/" + name },
|
||||
projectDir, result.result );
|
||||
return result;
|
||||
}
|
||||
|
||||
Git::Result Git::deleteTag( const std::string& name, const std::string& projectDir ) {
|
||||
Result result;
|
||||
result.returnCode = git( { "tag", "-d", name }, projectDir, result.result );
|
||||
|
||||
@@ -380,6 +380,9 @@ class Git {
|
||||
Result createTag( const std::string& name, const std::string& revision,
|
||||
const std::string& message = "", const std::string& projectDir = "" );
|
||||
|
||||
Result pushTag( const std::string& name, const std::string& remote,
|
||||
const std::string& projectDir = "" );
|
||||
|
||||
Result deleteTag( const std::string& name, const std::string& projectDir = "" );
|
||||
|
||||
Result deleteRemoteBranch( const std::string& remote, const std::string& branch,
|
||||
|
||||
@@ -2736,19 +2736,53 @@ void GitPlugin::addTag( const Git::Commit& commit ) {
|
||||
name->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent )
|
||||
->setLayoutMargin( Rectf( 0, 4, 0, 4 ) )
|
||||
->setParent( box->getTextEdit()->getParent() );
|
||||
name->setHint( i18n( "git_tag_name_hint", "Tag name" ) );
|
||||
name->toPosition( 1 );
|
||||
box->getTextEdit()->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::Fixed );
|
||||
box->getButtonOK()->setText( i18n( "git_add_tag", "Add Tag" ) );
|
||||
box->on( Event::OnConfirm, [this, box, name, commit]( const Event* ) {
|
||||
const std::string tag = name->getText().toUtf8();
|
||||
auto* message = box->getTextEdit();
|
||||
message->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::Fixed );
|
||||
message->setHint( i18n( "git_tag_message_hint", "Annotated tag message (optional)" ) );
|
||||
auto addTagBtn = box->getButtonOK();
|
||||
addTagBtn->setText( i18n( "git_add_tag", "Add Tag" ) );
|
||||
auto* addTagAndPushBtn = UIPushButton::New();
|
||||
addTagAndPushBtn->setText( i18n( "git_add_tag_and_push", "Add Tag & Push" ) )
|
||||
->setLayoutMargin( Rectf( 8, 0, 0, 0 ) )
|
||||
->setParent( addTagBtn->getParent() );
|
||||
box->getButtonCancel()->setLayoutMargin( {} )->toPosition( 0 );
|
||||
addTagBtn->setLayoutMargin( Rectf( 8, 0, 0, 0 ) )->toPosition( 1 );
|
||||
addTagAndPushBtn->toPosition( 2 );
|
||||
addTagBtn->setEnabled( false );
|
||||
addTagAndPushBtn->setEnabled( false );
|
||||
name->on( Event::OnTextChanged, [name, addTagBtn, addTagAndPushBtn]( const Event* ) {
|
||||
const bool enabled = !name->getText().empty();
|
||||
addTagBtn->setEnabled( enabled );
|
||||
addTagAndPushBtn->setEnabled( enabled );
|
||||
} );
|
||||
auto createTag = [this, box, name, message, commit]( bool push ) {
|
||||
std::string tag = name->getText().toUtf8();
|
||||
if ( tag.empty() )
|
||||
return;
|
||||
const std::string message = box->getTextEdit()->getText().toUtf8();
|
||||
std::string tagMessage = message->getText().toUtf8();
|
||||
std::string repo = repoSelected();
|
||||
std::string remote{ "origin" };
|
||||
if ( const auto branch = getBranchFromRepoPath( repo );
|
||||
branch && !branch->remote.empty() ) {
|
||||
const size_t separator = branch->remote.find( '/' );
|
||||
if ( separator != std::string::npos )
|
||||
remote = branch->remote.substr( 0, separator );
|
||||
}
|
||||
box->closeWindow();
|
||||
runAsync( [this, tag, message,
|
||||
commit] { return mGit->createTag( tag, commit.hash, message, repoSelected() ); },
|
||||
false, true );
|
||||
} );
|
||||
runAsync(
|
||||
[git = mGit, tag = std::move( tag ), tagMessage = std::move( tagMessage ),
|
||||
repo = std::move( repo ), remote = std::move( remote ), hash = commit.hash, push]() {
|
||||
auto result = git->createTag( tag, hash, tagMessage, repo );
|
||||
if ( result.success() && push )
|
||||
return git->pushTag( tag, remote, repo );
|
||||
return result;
|
||||
},
|
||||
false, true );
|
||||
};
|
||||
box->on( Event::OnConfirm, [createTag]( const Event* ) mutable { createTag( false ); } );
|
||||
addTagAndPushBtn->onClick( [createTag]( const Event* ) mutable { createTag( true ); } );
|
||||
box->setTitle( i18n( "git_add_tag", "Add Tag" ) );
|
||||
box->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } )->center();
|
||||
box->showWhenReady();
|
||||
|
||||
Reference in New Issue
Block a user