Fix in drop-down height calculation.

Added `--diff` parameter.
Allow to load multiple files and folders from the CLI.
This commit is contained in:
Martín Lucas Golini
2026-03-25 13:20:10 -03:00
parent d0093a7801
commit 527c1dc56c
8 changed files with 170 additions and 80 deletions

View File

@@ -32,7 +32,8 @@ class EE_API UIDiffView : public UIWidget {
virtual bool isType( const Uint32& type ) const override;
void loadFromPatch( const std::string& patchText, const std::string& originalFilePath = "" );
void loadFromStrings( const std::string& oldText, const std::string& newText );
void loadFromStrings( const std::string& oldText, const std::string& newText,
const std::string& originalFilePath = "" );
void loadFromFile( const std::string& oldFilePath, const std::string& newFilePath );
UICodeEditor* getEditor() const { return mEditor; }
@@ -110,6 +111,7 @@ class EE_API UIDiffView : public UIWidget {
void updateButtonsText();
};
}}} // namespace EE::UI::Tools
} // namespace Tools
}} // namespace EE::UI
#endif // EE_UI_TOOLS_UIDIFFVIEW_HPP

View File

@@ -790,7 +790,8 @@ void UIDiffView::loadFromPatch( const std::string& patchText,
onSizeChange();
}
void UIDiffView::loadFromStrings( const std::string& oldText, const std::string& newText ) {
void UIDiffView::loadFromStrings( const std::string& oldText, const std::string& newText,
const std::string& originalFilePath ) {
mLines.clear();
std::vector<std::string> leftLines = String::split( oldText, '\n', true );
@@ -828,9 +829,15 @@ void UIDiffView::loadFromStrings( const std::string& oldText, const std::string&
computeSubLineDiff( oldLine, newLine );
} );
mShowCompleteView = false;
mCompleteViewToggle->setText( i18n( "diffview_complete", "Complete" ) );
if ( !originalFilePath.empty() ) {
auto def = SyntaxDefinitionManager::instance()->getByExtension( originalFilePath );
mSyntaxDef =
SyntaxDefinitionManager::instance()->getLanguageDefinition( def.getLanguageIndex() );
mFileName = FileSystem::fileNameFromPath( originalFilePath );
}
updateEditorsText();
updateButtonsText();
onSizeChange();
}

View File

@@ -114,7 +114,7 @@ UIDropDownList* UIDropDownList::showList() {
Float height = std::ceil(
std::ceil( eemin( mListBox->getItemsCount(), mStyleConfig.MaxNumVisibleItems ) *
PixelDensity::pxToDp( mListBox->getRowHeight() ) ) +
mListBox->getRowHeight() ) +
tPadding.Top + tPadding.Bottom +
( mListBox->getHorizontalScrollBar() &&
mListBox->getHorizontalScrollBar()->isVisible() &&

View File

@@ -2696,6 +2696,25 @@ void App::loadDiffFromPath( const std::string& path ) {
diffView->setSyntaxColorScheme( *getCurrentColorScheme() );
}
void App::loadDiffFromPaths( const std::string& oldPath, const std::string& newPath ) {
auto diffViewTitle = i18n( "diff_viewer", "Diff Viewer" );
auto* diffView = Tools::UIDiffView::New();
auto [tab, iv] = mSplitter->createWidget( diffView, i18n( "diff_viewer", "Diff Viewer" ) );
if ( !newPath.empty() ) {
std::string fileName = FileSystem::fileNameFromPath( newPath );
tab->setText( diffViewTitle + ": " + fileName );
tab->setTooltipText( newPath );
} else {
tab->setText( diffViewTitle );
}
auto icon = findIcon( "filetype-diff" );
tab->setIcon( icon ? icon : findIcon( "file" ) );
diffView->setHeadersVisible( true );
diffView->loadFromFile( oldPath, newPath );
diffView->setSyntaxColorScheme( *getCurrentColorScheme() );
}
void App::openFileFromPath( const std::string& path ) {
std::string ext = FileSystem::fileExtension( path );
if ( !Image::isImageExtension( path ) && !SoundFileFactory::isKnownFileExtension( path ) &&
@@ -3637,79 +3656,107 @@ void App::initProjectTreeViewUI() {
mProjectTreeView->setAutoExpandOnSingleColumn( true );
}
void App::initProjectTreeView( std::string path, bool openClean ) {
void App::discardEmptyTab() {
// If we opened a new tab and the first tab is simply empty, discard it
if ( mSplitter->getTabWidgets().size() == 1 &&
mSplitter->getTabWidgets()[0]->getTabCount() == 2 &&
mSplitter->getTabWidgets()[0]->getTab( 0 ) ) {
auto tab = mSplitter->getTabWidgets()[0]->getTab( 0 );
if ( tab && tab->getOwnedWidget() && tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) {
auto editor = tab->getOwnedWidget()->asType<UICodeEditor>();
if ( editor->hasDocument() && editor->getDocument().isEmpty() ) {
mSplitter->getTabWidgets()[0]->removeTab( tab );
}
}
}
};
void App::initProjectTreeView( std::vector<std::string>&& paths, bool openClean ) {
initProjectTreeViewUI();
bool hasPosition = pathHasPosition( path );
TextPosition initialPosition;
if ( hasPosition ) {
auto pathAndPosition = getPathAndPosition( path );
path = pathAndPosition.first;
initialPosition = pathAndPosition.second;
}
const auto getInitialPosition = []( std::string& path ) -> TextPosition {
bool hasPosition = pathHasPosition( path );
TextPosition initialPosition;
if ( hasPosition ) {
auto pathAndPosition = getPathAndPosition( path );
path = pathAndPosition.first;
initialPosition = pathAndPosition.second;
}
return initialPosition;
};
if ( !path.empty() ) {
for ( auto& path : paths )
path = FileSystem::expandTilde( path );
if ( FileSystem::isDirectory( path ) ) {
loadFolder( path );
} else if ( String::startsWith( path, "https://" ) ||
String::startsWith( path, "http://" ) ) {
loadFolder( "." );
loadFileFromPath( path, false );
} else {
std::string rpath( FileSystem::getRealPath( path ) );
std::string folderPath( FileSystem::fileRemoveFileName( rpath ) );
if ( !paths.empty() ) {
bool openedFolder = false;
bool inNewTab = paths.size() != 1;
if ( FileSystem::isDirectory( folderPath ) ) {
loadFileSystemMatcher( folderPath );
for ( auto& path : paths ) {
if ( FileSystem::isDirectory( path ) ) {
loadFolder( path, openedFolder );
openedFolder = true;
} else if ( String::startsWith( path, "https://" ) ||
String::startsWith( path, "http://" ) ) {
if ( !openedFolder )
loadFolder( "." );
loadFileFromPath( path, inNewTab );
} else {
std::string rpath( FileSystem::getRealPath( paths[0] ) );
std::string folderPath( FileSystem::fileRemoveFileName( rpath ) );
mFileSystemModel =
FileSystemModel::New( folderPath, FileSystemModel::Mode::FilesAndDirectories,
{ true,
true,
true,
{},
[this]( const std::string& filePath ) -> bool {
return isFileVisibleInTreeView( filePath );
} },
&mUISceneNode->getTranslator(), mThreadPool );
if ( !inNewTab && FileSystem::isDirectory( folderPath ) ) {
loadFileSystemMatcher( folderPath );
mProjectTreeView->setModel( mFileSystemModel );
mProjectViewEmptyCont->setVisible( false );
mFileSystemModel = FileSystemModel::New(
folderPath, FileSystemModel::Mode::FilesAndDirectories,
{ true,
true,
true,
{},
[this]( const std::string& filePath ) -> bool {
return isFileVisibleInTreeView( filePath );
} },
&mUISceneNode->getTranslator(), mThreadPool );
if ( mFileSystemListener )
mFileSystemListener->setFileSystemModel( mFileSystemModel );
mProjectTreeView->setModel( mFileSystemModel );
mProjectViewEmptyCont->setVisible( false );
auto forcePosition = getForcePositionFn( initialPosition );
auto onLoaded = [this, forcePosition]( UICodeEditor* codeEditor,
const std::string& path ) {
if ( forcePosition )
forcePosition( codeEditor, path );
syncProjectTreeWithEditor( mSplitter->getCurEditor() );
};
if ( mFileSystemListener )
mFileSystemListener->setFileSystemModel( mFileSystemModel );
if ( FileSystem::fileExists( rpath ) ) {
loadFileFromPath( rpath, false, nullptr, onLoaded );
} else if ( FileSystem::fileCanWrite( folderPath ) ) {
loadFileFromPath( rpath, false, nullptr, onLoaded );
}
auto forcePosition = getForcePositionFn( getInitialPosition( paths[0] ) );
auto onLoaded = [this, forcePosition]( UICodeEditor* codeEditor,
const std::string& path ) {
if ( forcePosition )
forcePosition( codeEditor, path );
syncProjectTreeWithEditor( mSplitter->getCurEditor() );
};
// If we opened a new tab and the first tab is simply empty, discard it
if ( mSplitter->getTabWidgets().size() == 1 &&
mSplitter->getTabWidgets()[0]->getTabCount() == 2 &&
mSplitter->getTabWidgets()[0]->getTab( 0 ) ) {
auto tab = mSplitter->getTabWidgets()[0]->getTab( 0 );
if ( tab && tab->getOwnedWidget() &&
tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) {
auto editor = tab->getOwnedWidget()->asType<UICodeEditor>();
if ( editor->hasDocument() && editor->getDocument().isEmpty() ) {
mSplitter->getTabWidgets()[0]->removeTab( tab );
}
if ( FileSystem::fileExists( rpath ) ) {
loadFileFromPath( rpath, false, nullptr, onLoaded );
} else if ( FileSystem::fileCanWrite( folderPath ) ) {
loadFileFromPath( rpath, false, nullptr, onLoaded );
}
discardEmptyTab();
mSettings->updateProjectSettingsMenu();
} else {
auto forcePosition = getForcePositionFn( getInitialPosition( path ) );
auto onLoaded = [this, forcePosition]( UICodeEditor* codeEditor,
const std::string& path ) {
if ( forcePosition )
forcePosition( codeEditor, path );
syncProjectTreeWithEditor( mSplitter->getCurEditor() );
};
if ( FileSystem::fileExists( rpath ) ) {
loadFileFromPath( rpath, inNewTab, nullptr, onLoaded );
} else if ( FileSystem::fileCanWrite( folderPath ) ) {
loadFileFromPath( rpath, inNewTab, nullptr, onLoaded );
}
}
mSettings->updateProjectSettingsMenu();
}
}
} else if ( mConfig.workspace.restoreLastSession && !mRecentFolders.empty() && !openClean ) {
@@ -4160,7 +4207,8 @@ void App::init( InitParameters& params ) {
if ( params.prematureExit )
return;
if ( !params.openClean && needsRedirectToRunningProcess( params.file ) )
if ( !params.openClean && params.files.size() == 1 &&
needsRedirectToRunningProcess( params.files[0] ) )
return;
currentDisplay = displayManager->getDisplayIndex( mConfig.windowState.displayIndex <
@@ -4793,7 +4841,7 @@ void App::init( InitParameters& params ) {
params.file = "";
#endif
if ( params.terminal && params.file.empty() && params.fileToOpen.empty() ) {
if ( params.terminal && params.files.empty() && params.fileToOpen.empty() ) {
mTerminalMode = true;
mTerminalModeSidePanelWasVisible = mConfig.ui.showSidePanel;
mConfig.ui.showSidePanel = false;
@@ -4803,8 +4851,11 @@ void App::init( InitParameters& params ) {
getSettingsMenu()->updateViewMenu();
initProjectViewEmptyCont();
mTerminalManager->createNewTerminal();
} else if ( params.diff ) {
loadDiffFromPaths( params.files[0], params.files[1] );
discardEmptyTab();
} else {
initProjectTreeView( params.file, params.openClean );
initProjectTreeView( std::move( params.files ), params.openClean );
}
mFileToOpen = FileSystem::expandTilde( params.fileToOpen );
@@ -4886,8 +4937,8 @@ using namespace ecode;
EE_MAIN_FUNC int main( int argc, char* argv[] ) {
args::ArgumentParser parser( "ecode" );
args::HelpFlag help( parser, "help", "Display this help menu", { 'h', '?', "help" } );
args::Positional<std::string> fileOrFolderPos( parser, "file_or_folder",
"The file or folder path to open" );
args::PositionalList<std::string> fileOrFolderPos( parser, "file_or_folder",
"The file/s or folder/s path to open" );
args::ValueFlag<std::string> file(
parser, "file",
"The file path to open. A file path can also contain the line number and column to "
@@ -4984,6 +5035,9 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
"the earliest launched instance instead of the most recently launched one.",
{ "first-instance" } );
args::Flag diff( parser, "diff <OLD_PATH> <NEW_PATH>", "Pairs of file paths to diff.",
{ "diff" } );
#ifdef EE_TEXT_SHAPER_ENABLED
args::Flag textShaper( parser, "text-shaper",
"WARNING: Do not use this option. It will be completely "
@@ -5054,7 +5108,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
App::InitParameters params;
params.logLevel = logLevel.Get();
params.file = folder ? folder.Get() : fileOrFolderPos.Get();
params.files = folder ? std::vector<std::string>{ folder.Get() } : fileOrFolderPos.Get();
params.pidelDensity = pixelDensityConf ? pixelDensityConf.Get() : 0.f;
params.colorScheme = prefersColorScheme ? prefersColorScheme.Get() : "";
params.terminal = terminal.Get();
@@ -5073,6 +5127,26 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
params.profile = profile.Get();
params.disablePlugins = disablePlugins.Get();
params.redirectToFirstInstance = redirectToFirstInstance.Get();
params.diff = diff.Get();
if ( params.diff ) {
if ( params.files.size() != 2 ) {
Sys::windowAttachConsole();
std::cout << "error: 2 values required for '--diff <OLD_PATH> <NEW_PATH>' but "
<< params.files.size() << " was provided" << std::endl;
return EXIT_FAILURE;
}
for ( size_t i = 0; i < params.files.size(); i++ ) {
if ( !FileSystem::fileExists( params.files[i] ) ||
FileSystem::isDirectory( params.files[i] ) ) {
Sys::windowAttachConsole();
std::cout << "error: invalid parameter " << ( i + 1 )
<< " for '--diff <OLD_PATH> <NEW_PATH>' " << std::endl;
return EXIT_FAILURE;
}
}
}
appInstance = eeNew( App, ( jobs, args ) );
appInstance->init( params );

View File

@@ -43,24 +43,25 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
struct InitParameters {
LogLevel logLevel{ LogLevel::Info };
std::string file;
std::vector<std::string> files;
Float pidelDensity{ 0.f };
std::string colorScheme;
std::string css;
std::string fileToOpen;
std::string language;
std::string profile;
bool terminal{ false };
bool frameBuffer{ false };
bool benchmarkMode{ false };
std::string css;
std::string fileToOpen;
bool stdOutLogs{ false };
bool disableFileLogs{ false };
bool openClean{ false };
bool portable{ false };
std::string language;
bool incognito{ false };
bool prematureExit{ false };
std::string profile;
bool disablePlugins{ false };
bool redirectToFirstInstance{ false };
bool diff{ false };
};
void init( InitParameters& );
@@ -558,6 +559,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
void loadDiffFromPath( const std::string& path );
void loadDiffFromPaths( const std::string& oldPath, const std::string& newPath );
void loadDiffFromMemory( const std::string& content, const std::string& originalFilePath = "" );
void createAndShowRecentFolderPopUpMenu( Node* recentFoldersBut );
@@ -759,7 +762,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
void initProjectTreeViewUI();
void initProjectTreeView( std::string path, bool openClean );
void initProjectTreeView( std::vector<std::string>&& paths, bool openClean );
void initImageView();
@@ -868,6 +871,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
std::string firstInstanceIndicatorPath() const;
void tintTitleBar();
void discardEmptyTab();
};
} // namespace ecode

View File

@@ -797,9 +797,9 @@ void DebuggerPlugin::buildSidePanelTab() {
<vbox id="debugger_panel" lw="mp" lh="wc" padding="4dp">
<vbox id="debugger_config_view" lw="mp" lh="wc">
<TextView text="@string(debugger, Debugger)" font-size="15dp" focusable="false" />
<DropDownList id="debugger_list" layout_width="mp" layout_height="wrap_content" margin-top="2dp" />
<DropDownList id="debugger_list" layout_width="mp" layout_height="wrap_content" margin-top="2dp" menu-width-mode="expand-if-needed" />
<TextView text="@string(debugger_configuration, Debugger Configuration)" focusable="false" margin-top="8dp" />
<DropDownList id="debugger_conf_list" layout_width="mp" layout_height="wrap_content" margin-top="2dp" />
<DropDownList id="debugger_conf_list" layout_width="mp" layout_height="wrap_content" margin-top="2dp" menu-width-mode="expand-if-needed" />
<PushButton id="debugger_run_button" lw="mp" lh="wc" text="@string(debug, Debug)" margin-top="8dp" icon="icon(debug-alt, 12dp)" />
<PushButton id="debugger_build_and_run_button" lw="mp" lh="wc" text="@string(build_and_debug, Build & Debug)" margin-top="8dp" icon="icon(debug-alt, 12dp)" />
<hbox id="panel_debugger_buttons" lw="wc" lh="wc" layout_gravity="center_horizontal" visible="false" clip="none" margin-top="8dp">

View File

@@ -1459,7 +1459,7 @@ void GitPlugin::buildSidePanelTab() {
<StackWidget id="git_panel_stack" lw="mp" lh="0" lw8="1">
<vbox id="git_branches" lw="mp" lh="wc">
<hbox lw="mp" lh="wc" padding="4dp">
<DropDownList id="git_repo" lw="0" lh="wc" lw8="1" />
<DropDownList id="git_repo" lw="0" lh="wc" lw8="1" menu-width-mode="expand-if-needed" />
<PushButton id="branch_pull" text="@string(git_pull, Pull)" tooltip="@string(pull_branch, Pull Branch)" text-as-fallback="true" icon="icon(repo-pull, 12dp)" margin-left="2dp" />
<PushButton id="branch_push" text="@string(git_push, Push)" tooltip="@string(push_branch, Push Branch)" text-as-fallback="true" icon="icon(repo-push, 12dp)" margin-left="2dp" />
<PushButton id="branch_add" text="@string(git_add_branch, Add Branch)" tooltip="@string(add_branch, Add Branch)" text-as-fallback="true" icon="icon(add, 12dp)" margin-left="2dp" />

View File

@@ -148,6 +148,8 @@ class PluginContextProvider {
virtual void loadImageFromPath( const std::string& path ) = 0;
virtual void loadDiffFromPaths( const std::string& oldPath, const std::string& newPath ) = 0;
virtual void loadDiffFromPath( const std::string& path ) = 0;
virtual void loadDiffFromMemory( const std::string& content,