mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-18 06:55:48 +03:00
Fixes in UICodeEditor mouse click in gutter.
Added some basic culling in UICodeEditor. Remember the Chat UI input text.
This commit is contained in:
@@ -118,6 +118,7 @@ class EE_API Event {
|
||||
OnWindowToFront,
|
||||
OnVisibleLinesCountChange,
|
||||
OnDataChanged,
|
||||
OnFoldUnfoldRange,
|
||||
NoEvent = eeINDEX_NOT_FOUND
|
||||
};
|
||||
|
||||
|
||||
@@ -157,6 +157,8 @@ class EE_API DocumentView {
|
||||
|
||||
void setOnVisibleLineCountChange( std::function<void()> onVisibleLinesCountChangeCb );
|
||||
|
||||
void setOnFoldUnfoldCb( std::function<void(Int64 docIdx, bool unfolded)> onFoldUnfoldCb );
|
||||
|
||||
protected:
|
||||
std::shared_ptr<TextDocument> mDoc;
|
||||
FontStyleConfig mFontStyle;
|
||||
@@ -171,6 +173,7 @@ class EE_API DocumentView {
|
||||
bool mUnderConstruction{ false };
|
||||
bool mUpdatingFoldRegions{ false };
|
||||
std::function<void()> mOnVisibleLineCountChange;
|
||||
std::function<void(Int64 docIdx, bool unfolded)> mOnFoldUnfoldCb;
|
||||
|
||||
void changeVisibility( Int64 fromDocIdx, Int64 toDocIdx, bool visible,
|
||||
bool recomputeOffset = true, bool recomputeLineToVisibleIndex = true );
|
||||
|
||||
@@ -792,6 +792,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
|
||||
bool isCursorBlinkingAfterAMinuteOfInactivityDisabled() const;
|
||||
|
||||
void setAllowSelectingTextFromGutter( bool allow );
|
||||
|
||||
bool allowSelectingTextFromGutter() const;
|
||||
|
||||
protected:
|
||||
struct LastXOffset {
|
||||
TextPosition position{ 0, 0 };
|
||||
@@ -841,6 +845,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
bool mFoldsIsFirst{ true };
|
||||
bool mEnableFlashCursor{ false };
|
||||
bool mDisableCursorBlinkingAfterAMinuteOfInactivity{ true };
|
||||
bool mAllowSelectingTextFromGutter{ true };
|
||||
Uint32 mTabWidth;
|
||||
std::atomic<size_t> mHighlightWordProcessing{ false };
|
||||
TextRange mLinkPosition;
|
||||
|
||||
@@ -814,4 +814,9 @@ void DocumentView::setOnVisibleLineCountChange(
|
||||
mOnVisibleLineCountChange = std::move( onVisibleLinesCountChangeCb );
|
||||
}
|
||||
|
||||
void DocumentView::setOnFoldUnfoldCb(
|
||||
std::function<void( Int64 docIdx, bool unfolded )> onFoldUnfoldCb ) {
|
||||
mOnFoldUnfoldCb = std::move( onFoldUnfoldCb );
|
||||
}
|
||||
|
||||
}}} // namespace EE::UI::Doc
|
||||
|
||||
@@ -140,6 +140,8 @@ UICodeEditor::UICodeEditor( const std::string& elementTag, const bool& autoRegis
|
||||
refreshTag();
|
||||
mDocView.setOnVisibleLineCountChange(
|
||||
[this] { sendCommonEvent( Event::OnVisibleLinesCountChange ); } );
|
||||
mDocView.setOnFoldUnfoldCb(
|
||||
[this]( auto, auto ) { sendCommonEvent( Event::OnFoldUnfoldRange ); } );
|
||||
mVScrollBar = UIScrollBar::NewVertical();
|
||||
mVScrollBar->setParent( this );
|
||||
mVScrollBar->addEventListener( Event::OnSizeChange,
|
||||
@@ -338,6 +340,13 @@ void UICodeEditor::draw() {
|
||||
drawLineEndings( lineRange, startScroll, lineHeight );
|
||||
}
|
||||
|
||||
const bool canCull = !isMeOrParentTreeScaledOrRotated();
|
||||
const auto maxScreenY = isClipped()
|
||||
? eemin( getUISceneNode()->getPixelsSize().getHeight(),
|
||||
mScreenPos.y + mSize.getHeight() ) +
|
||||
lineHeight
|
||||
: getUISceneNode()->getPixelsSize().getHeight() + lineHeight;
|
||||
|
||||
for ( auto i = lineRange.first; i <= lineRange.second; i++ ) {
|
||||
if ( !mDocView.isLineVisible( i ) )
|
||||
continue;
|
||||
@@ -346,6 +355,14 @@ void UICodeEditor::draw() {
|
||||
{ startScroll.x,
|
||||
static_cast<Float>( startScroll.y + mDocView.getLineYOffset( i, lineHeight ) ) } );
|
||||
|
||||
if ( canCull ) {
|
||||
if ( curScroll.y < -lineHeight && !mDocView.isWrappedLine( i ) )
|
||||
continue;
|
||||
|
||||
if ( curScroll.y > maxScreenY )
|
||||
break;
|
||||
}
|
||||
|
||||
for ( auto& plugin : mPlugins )
|
||||
plugin->drawBeforeLineText( this, i, curScroll, charSize, lineHeight );
|
||||
|
||||
@@ -1597,8 +1614,9 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags
|
||||
mLastDoubleClick.getElapsedTime() < Milliseconds( 300.f ) ) {
|
||||
mDoc->selectLine();
|
||||
} else {
|
||||
mDoc->setSelection( textScreenPos );
|
||||
if ( downOverGutter )
|
||||
if ( !downOverGutter || mAllowSelectingTextFromGutter )
|
||||
mDoc->setSelection( textScreenPos );
|
||||
if ( downOverGutter && mAllowSelectingTextFromGutter )
|
||||
mDoc->selectLine();
|
||||
}
|
||||
} else if ( !mDoc->hasSelection() ) {
|
||||
@@ -1662,10 +1680,11 @@ Uint32 UICodeEditor::onMouseMove( const Vector2i& position, const Uint32& flags
|
||||
}
|
||||
|
||||
Vector2f localPos( convertToNodeSpace( position.asFloat() ) );
|
||||
bool downOverGutter = localPos.x < mPaddingPx.Left + getGutterWidth();
|
||||
|
||||
if ( !mMouseDownMinimap && isTextSelectionEnabled() &&
|
||||
!getUISceneNode()->getEventDispatcher()->isNodeDragging() && NULL != mFont &&
|
||||
mMouseDown ) {
|
||||
!getUISceneNode()->getEventDispatcher()->isNodeDragging() && NULL != mFont && mMouseDown &&
|
||||
( !downOverGutter || mAllowSelectingTextFromGutter ) ) {
|
||||
TextRange selection = mDoc->getSelection();
|
||||
selection.setStart( resolveScreenPosition( position.asFloat() ) );
|
||||
mDoc->setSelection( selection );
|
||||
@@ -1794,7 +1813,9 @@ Uint32 UICodeEditor::onMouseDoubleClick( const Vector2i& position, const Uint32&
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( isTextSelectionEnabled() && ( flags & EE_BUTTON_LMASK ) ) {
|
||||
Vector2f localPos( convertToNodeSpace( position.asFloat() ) );
|
||||
if ( isTextSelectionEnabled() && ( flags & EE_BUTTON_LMASK ) &&
|
||||
localPos.x >= mPaddingPx.Left + getGutterWidth() && localPos.y >= mPluginsTopSpace ) {
|
||||
mDoc->selectWord( false );
|
||||
mLastDoubleClick.restart();
|
||||
checkColorPickerAction();
|
||||
@@ -5254,7 +5275,8 @@ bool UICodeEditor::isNotMonospace() const {
|
||||
void UICodeEditor::updateMouseCursor( const Vector2f& position ) {
|
||||
if ( getScreenBounds().contains( position ) ) {
|
||||
auto localPos( convertToNodeSpace( position ) );
|
||||
bool overGutterOrTop = localPos.x < getGutterWidth() || localPos.y < mPluginsTopSpace;
|
||||
bool overGutterOrTop =
|
||||
localPos.x < mPaddingPx.Left + getGutterWidth() || localPos.y < mPluginsTopSpace;
|
||||
getUISceneNode()->setCursor(
|
||||
mHandShown ? Cursor::Hand
|
||||
: ( !overGutterOrTop && !mLocked ? Cursor::IBeam : Cursor::Arrow ) );
|
||||
@@ -5315,4 +5337,12 @@ bool UICodeEditor::isCursorBlinkingAfterAMinuteOfInactivityDisabled() const {
|
||||
return mDisableCursorBlinkingAfterAMinuteOfInactivity;
|
||||
}
|
||||
|
||||
void UICodeEditor::setAllowSelectingTextFromGutter( bool allow ) {
|
||||
mAllowSelectingTextFromGutter = allow;
|
||||
}
|
||||
|
||||
bool UICodeEditor::allowSelectingTextFromGutter() const {
|
||||
return mAllowSelectingTextFromGutter;
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -186,17 +186,23 @@ void AIAssistantPlugin::load( PluginManager* pluginManager ) {
|
||||
filePath = conversationsPath + *foundIt;
|
||||
}
|
||||
|
||||
std::string inputText;
|
||||
if ( !filePath.empty() ) {
|
||||
std::string data;
|
||||
FileSystem::fileGet( filePath, data );
|
||||
if ( !data.empty() ) {
|
||||
auto j = nlohmann::json::parse( data, nullptr, false );
|
||||
if ( !j.empty() )
|
||||
chatUI->unserialize( j );
|
||||
if ( !j.empty() ){
|
||||
inputText = chatUI->unserialize( j );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chatUI->on( Event::OnDataChanged, [chatUI]( auto ) { chatUI->updateTabTitle(); } );
|
||||
chatUI->on( Event::OnDataChanged, [chatUI, inputText = std::move( inputText )]( auto ) {
|
||||
chatUI->updateTabTitle();
|
||||
if ( chatUI->getChatInput() )
|
||||
chatUI->getChatInput()->getDocument().textInput( inputText );
|
||||
} );
|
||||
}
|
||||
|
||||
return TabWidgetData{ chatUI, getPluginContext()->findIcon( "code-ai" ),
|
||||
|
||||
@@ -138,7 +138,7 @@ DropDownList.role_ui {
|
||||
<TextView text="@string(ai_llm_presentation, What can I help with?)" font-size="24dp" />
|
||||
</vbox>
|
||||
</RelativeLayout>
|
||||
<RelativeLayout lw="mp" class="llm_controls">
|
||||
<RelativeLayout lw="mp" class="llm_controls" clip="true">
|
||||
<CodeEditor class="llm_chat_input" lw="mp" lh="mp" />
|
||||
<hbox lw="mp" lh="wc" layout_gravity="bottom|left" layout_margin="8dp" clip="false">
|
||||
<PushButton id="llm_user" class="llm_button" text="@string(user, User)" min-width="60dp" margin-right="8dp" />
|
||||
@@ -220,6 +220,7 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
mChatInput->getDocument().getFoldRangeService().setEnabled( true );
|
||||
mChatInput->setFoldDrawable( findIcon( "chevron-down", PixelDensity::dpToPxI( 12 ) ) );
|
||||
mChatInput->setFoldedDrawable( findIcon( "chevron-right", PixelDensity::dpToPxI( 12 ) ) );
|
||||
mChatInput->setAllowSelectingTextFromGutter( false );
|
||||
|
||||
mChatInput->setSyntaxDefinition( markdown );
|
||||
|
||||
@@ -311,10 +312,12 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
return;
|
||||
}
|
||||
auto* chatUI = getPlugin()->newAIAssistant();
|
||||
chatUI->unserialize( serialize() );
|
||||
auto input = chatUI->unserialize( serialize() );
|
||||
chatUI->mUUID = UUID();
|
||||
chatUI->mSummary += i18n( "chat_cloned", " (cloned)" );
|
||||
updateTabTitle();
|
||||
if ( !input.empty() )
|
||||
chatUI->mChatInput->getDocument().textInput( input );
|
||||
chatUI->updateTabTitle();
|
||||
chatUI->setFocus();
|
||||
} );
|
||||
|
||||
@@ -517,7 +520,9 @@ void LLMChatUI::showChatHistory() {
|
||||
std::string data;
|
||||
FileSystem::fileGet( path.toString(), data );
|
||||
nlohmann::json j = nlohmann::json::parse( data, nullptr, false );
|
||||
chatUI->unserialize( j );
|
||||
auto input = chatUI->unserialize( j );
|
||||
if ( !input.empty() )
|
||||
chatUI->mChatInput->getDocument().textInput( input );
|
||||
chatUI->setFocus();
|
||||
};
|
||||
|
||||
@@ -737,10 +742,12 @@ nlohmann::json LLMChatUI::serialize() {
|
||||
j["provider"] = mCurModel.provider;
|
||||
j["timestamp"] = mTimestamp;
|
||||
j["summary"] = mSummary;
|
||||
std::string inputText( mChatInput->getDocument().getText().toUtf8() );
|
||||
j["input"] = std::move( inputText );
|
||||
return j;
|
||||
}
|
||||
|
||||
void LLMChatUI::unserialize( const nlohmann::json& payload ) {
|
||||
std::string LLMChatUI::unserialize( const nlohmann::json& payload ) {
|
||||
auto uuid = UUID::fromString( payload.value( "uuid", "" ) );
|
||||
if ( uuid )
|
||||
mUUID = *uuid;
|
||||
@@ -755,7 +762,7 @@ void LLMChatUI::unserialize( const nlohmann::json& payload ) {
|
||||
}
|
||||
|
||||
if ( mCurModel.name.empty() )
|
||||
return;
|
||||
return payload.value( "input", "" );
|
||||
|
||||
if ( !selectModel( mModelDDL, mCurModel ) )
|
||||
fillModelDropDownList( mModelDDL );
|
||||
@@ -770,6 +777,8 @@ void LLMChatUI::unserialize( const nlohmann::json& payload ) {
|
||||
}
|
||||
|
||||
updateTabTitle();
|
||||
|
||||
return payload.value( "input", "" );
|
||||
}
|
||||
|
||||
LLMModel LLMChatUI::findModel( const std::string& provider, const std::string& model ) {
|
||||
@@ -843,7 +852,7 @@ void LLMChatUI::doRequest() {
|
||||
auto* thinking = editor->findByClass<UIImage>( "thinking" );
|
||||
auto thinkingID = String::hash( String::format( "thinking-%p", thinking ) );
|
||||
thinking->setVisible( true );
|
||||
thinking->setPosition( { PixelDensity::dpToPx( 16 ), PixelDensity::dpToPx( 4 ) } );
|
||||
thinking->setPosition( { PixelDensity::dpToPx( 8 ), PixelDensity::dpToPx( 4 ) } );
|
||||
thinking->setInterval( [thinking] { thinking->rotate( 360 / 32 ); }, Seconds( 0.125 ),
|
||||
thinkingID );
|
||||
|
||||
@@ -1035,6 +1044,7 @@ void LLMChatUI::addChat( LLMChat::Role role, std::string conversation ) {
|
||||
auto* editor = chat->findByClass<UICodeEditor>( "data_ui" );
|
||||
editor->getDocument().textInput( String::fromUtf8( conversation ) );
|
||||
editor->setCursorVisible( false );
|
||||
editor->setAllowSelectingTextFromGutter( false );
|
||||
resizeToFit( editor );
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
||||
|
||||
nlohmann::json serialize();
|
||||
|
||||
void unserialize( const nlohmann::json& payload );
|
||||
std::string unserialize( const nlohmann::json& payload ); // returns the input value
|
||||
|
||||
UISplitter* getSplitter() const;
|
||||
|
||||
@@ -78,6 +78,8 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
||||
return WidgetCommandExecuter::onKeyDown( event );
|
||||
}
|
||||
|
||||
UICodeEditor* getChatInput() const { return mChatInput; }
|
||||
|
||||
protected:
|
||||
UUID mUUID;
|
||||
std::string mSummary;
|
||||
|
||||
Reference in New Issue
Block a user