Added keyboard shortcut operations in the file system tree view, to complete SpartanJ/ecode#71.

This commit is contained in:
Martín Lucas Golini
2025-09-07 19:48:29 -03:00
parent 873274e33c
commit 7e6de3f9b2
10 changed files with 160 additions and 68 deletions

View File

@@ -39,9 +39,9 @@ class EE_API FileSystem {
*/
static bool fileGet( const std::string& path, std::string& data );
/** Copy a file to location.
* @param src Source File Path
* @param dst Destination File Path
/** Copy a file or a directory (recursively) to location.
* @param src Source file / directory Path
* @param dst Destination file path
* @return If success.
*/
static bool fileCopy( const std::string& src, const std::string& dst );
@@ -79,6 +79,12 @@ class EE_API FileSystem {
/** Deletes a file from the file system. */
static bool fileRemove( const std::string& filepath );
/** Moves a file or folder to the destination path
* @param fromPath The path of the file or folder to move
* @param toPath The folder / directory destination path
*/
static bool fileMove( const std::string& fromPath, const std::string& toPath );
/** Hides the file in the file system */
static bool fileHide( const std::string& filepath );
@@ -172,6 +178,7 @@ class EE_API FileSystem {
/** Opens a file path with a path and mode encoded in UTF-8 */
static FILE* fopenUtf8( const std::string& path, const std::string& mode );
};
}} // namespace EE::System

View File

@@ -4,6 +4,7 @@
#include <eepp/system/iostreamfile.hpp>
#include <eepp/system/sys.hpp>
#include <sys/stat.h>
#include <filesystem>
#if EE_PLATFORM == EE_PLATFORM_WIN
#ifndef WIN32_LEAN_AND_MEAN
@@ -93,38 +94,32 @@ bool FileSystem::fileGet( const std::string& path, std::string& data ) {
}
bool FileSystem::fileCopy( const std::string& src, const std::string& dst ) {
if ( fileExists( src ) ) {
Int64 chunksize = EE_1MB;
Int64 size = fileSize( src );
Int64 size_left = (Int32)size;
Int64 allocate = ( size < chunksize ) ? size : chunksize;
Int64 copysize = 0;
try {
#if EE_PLATFORM == EE_PLATFORM_WIN
std::filesystem::path source = String( src ).toWideString();
std::filesystem::path destination = String( dst ).toWideString();
#else
std::filesystem::path source = src;
std::filesystem::path destination = dst;
#endif
TScopedBuffer<char> data( allocate );
char* buff = data.get();
if ( !std::filesystem::exists( source ) )
return false;
IOStreamFile in( src, "rb" );
IOStreamFile out( dst, "wb" );
if ( in.isOpen() && out.isOpen() && size > 0 ) {
do {
if ( size_left - chunksize < 0 ) {
copysize = size_left;
} else {
copysize = chunksize;
}
in.read( &buff[0], copysize );
out.write( &buff[0], copysize );
size_left -= copysize;
} while ( size_left > 0 );
return true;
if ( std::filesystem::is_directory( source ) ) {
std::filesystem::copy( source, destination,
std::filesystem::copy_options::recursive |
std::filesystem::copy_options::overwrite_existing );
} else {
std::filesystem::copy( source, destination,
std::filesystem::copy_options::overwrite_existing );
}
return true;
} catch ( const std::filesystem::filesystem_error& ) {
return false;
} catch ( const std::exception& ) {
return false;
}
return false;
}
std::string FileSystem::fileExtension( const std::string& filepath, const bool& lowerExt ) {
@@ -710,4 +705,19 @@ FILE* FileSystem::fopenUtf8( const std::string& path, const std::string& mode )
return fopenUtf8( path.c_str(), mode.c_str() );
}
bool FileSystem::fileMove( const std::string& fpath, const std::string& newFilePath ) {
try {
#if EE_PLATFORM == EE_PLATFORM_WIN
std::filesystem::rename( String( fpath ).toWideString(),
String( newFilePath ).toWideString() );
#else
std::filesystem::rename( fpath, newFilePath );
#endif
} catch ( const std::filesystem::filesystem_error& ) {
return false;
}
return true;
}
}} // namespace EE::System

View File

@@ -1,4 +1,5 @@
#include "ecode.hpp"
#include "customwidgets.hpp"
#include "featureshealth.hpp"
#include "keybindingshelper.hpp"
#include "pathhelper.hpp"

View File

@@ -2,7 +2,6 @@
#define ECODE_HPP
#include "appconfig.hpp"
#include "customwidgets.hpp"
#include "docsearchcontroller.hpp"
#include "featureshealth.hpp"
#include "filesystemlistener.hpp"

View File

@@ -8,8 +8,15 @@ using namespace EE::Scene;
namespace ecode {
NotificationCenter* sInstance = nullptr;
NotificationCenter* NotificationCenter::instance() {
return sInstance;
}
NotificationCenter::NotificationCenter( UILayout* layout, PluginManager* pluginManager ) :
mLayout( layout ), mPluginManager( pluginManager ) {
sInstance = this;
mPluginManager->subscribeMessages(
"notificationcenter", [this]( const PluginMessage& msg ) -> PluginRequestHandle {
if ( !msg.isBroadcast() )

View File

@@ -7,6 +7,8 @@ namespace ecode {
class NotificationCenter {
public:
static NotificationCenter* instance();
NotificationCenter( UILayout* layout, PluginManager* pluginManager );
void addNotification( const String& text, const Time& delay = Seconds( 2.5 ),

View File

@@ -1306,26 +1306,11 @@ void LLMChatUI::updateTabTitle() {
tab->setText( title );
}
static bool fsRenameFile( const std::string& fpath, const std::string& newFilePath ) {
try {
#if EE_PLATFORM == EE_PLATFORM_WIN
std::filesystem::rename( String( fpath ).toWideString(),
String( newFilePath ).toWideString() );
#else
std::filesystem::rename( fpath, newFilePath );
#endif
} catch ( const std::filesystem::filesystem_error& ) {
return false;
}
return true;
}
void LLMChatUI::renameChat( const std::string& newName, bool invertLockedState ) {
auto oldPath = getNewFilePath( mUUID.toString(), mSummary, mChatLocked );
auto newPath =
getNewFilePath( mUUID.toString(), newName, invertLockedState ? !mChatLocked : mChatLocked );
if ( fsRenameFile( oldPath, newPath ) ) {
if ( FileSystem::fileMove( oldPath, newPath ) ) {
mSummary = newName;
if ( invertLockedState )
mChatLocked = !mChatLocked;

View File

@@ -1,15 +1,22 @@
#include "uitreeviewfs.hpp"
#include "customwidgets.hpp"
#include "notificationcenter.hpp"
#include <eepp/system/filesystem.hpp>
#include <eepp/ui/models/filesystemmodel.hpp>
#include <eepp/ui/uimessagebox.hpp>
#include <eepp/window/cursormanager.hpp>
#include <filesystem>
namespace ecode {
static const std::map<KeyBindings::Shortcut, std::string> getDefaultKeybindings() {
return {
{ { KEY_C, KeyMod::getDefaultModifier() }, "copy" },
{ { KEY_X, KeyMod::getDefaultModifier() }, "cut" },
{ { KEY_V, KeyMod::getDefaultModifier() }, "paste" },
};
}
class UITreeViewCellFS : public UITreeViewCell {
public:
static UITextView* sDragTV;
@@ -30,7 +37,9 @@ class UITreeViewCellFS : public UITreeViewCell {
return widget->isType( static_cast<Uint32>( CustomWidgets::UI_TYPE_TREEVIEWCELLFS ) );
}
UITreeViewFS* getTreeView() const { return getParent()->getParent()->asType<UITreeViewFS>(); }
inline UITreeViewFS* getTreeView() const {
return getParent()->getParent()->asType<UITreeViewFS>();
}
const FileSystemModel* getModel() const {
auto model = getParent()->getParent()->asType<UITreeView>()->getModel();
@@ -95,6 +104,14 @@ class UITreeViewCellFS : public UITreeViewCell {
getEventDispatcher()->setNodeDragging( nullptr );
return 1;
}
std::string cmd = getTreeView()->getKeyBindings().getCommandFromKeyBind(
{ event.getKeyCode(), event.getMod() } );
if ( !cmd.empty() ) {
getTreeView()->execute( cmd );
return 1;
}
return 0;
}
@@ -108,7 +125,33 @@ class UITreeViewCellFS : public UITreeViewCell {
UITextView* UITreeViewCellFS::sDragTV = nullptr;
UITreeViewFS::UITreeViewFS() : UITreeView() {}
UITreeViewFS::UITreeViewFS() : UITreeView(), mKeyBindings( getInput() ) {
mCommands["copy"] = [this] {
if ( getSelection().isEmpty() )
return;
mSrcCopy = getSelectionPath();
mWasCut = false;
NotificationCenter::instance()->addNotification(
String::format( i18n( "copied_file", "Copied '%s'" ).toUtf8(),
FileSystem::fileNameFromPath( mSrcCopy ) ) );
};
mCommands["cut"] = [this] {
if ( getSelection().isEmpty() )
return;
mSrcCopy = getSelectionPath();
mWasCut = true;
NotificationCenter::instance()->addNotification( String::format(
i18n( "cut_file", "Cut '%s'" ).toUtf8(), FileSystem::fileNameFromPath( mSrcCopy ) ) );
};
mCommands["paste"] = [this] {
if ( mWasCut )
moveFile( mSrcCopy, getSelectionPath() );
};
mKeyBindings.addKeybinds( getDefaultKeybindings() );
}
UIWidget* UITreeViewFS::createCell( UIWidget* rowWidget, const ModelIndex& index ) {
auto* widget = UITreeViewCellFS::New();
@@ -130,21 +173,6 @@ Uint32 UITreeViewFS::onMessage( const NodeMessage* msg ) {
return UITreeView::onMessage( msg );
}
static bool fsRenameFile( const std::string& fpath, const std::string& newFilePath ) {
try {
#if EE_PLATFORM == EE_PLATFORM_WIN
std::filesystem::rename( String( fpath ).toWideString(),
String( newFilePath ).toWideString() );
#else
std::filesystem::rename( fpath, newFilePath );
#endif
} catch ( const std::filesystem::filesystem_error& ) {
return false;
}
return true;
}
void UITreeViewFS::moveFile( const std::string& src, const std::string& dst ) {
FileInfo srcInfo( src );
if ( !srcInfo.exists() )
@@ -179,11 +207,48 @@ void UITreeViewFS::moveFile( const std::string& src, const std::string& dst ) {
partialSrc, partialDst ) );
UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::OK_CANCEL, confirmMsg );
msgBox->setTitle( "ecode" );
msgBox->center();
msgBox->setCloseShortcut( { KEY_ESCAPE, 0 } );
msgBox->showWhenReady();
msgBox->on( Event::OnConfirm, [srcPath = std::move( srcPath ), dstPath = std::move( dstPath )](
auto ) { fsRenameFile( srcPath, dstPath ); } );
auto ) { FileSystem::fileMove( srcPath, dstPath ); } );
}
void UITreeViewFS::copyFile( const std::string& src, const std::string& dst ) {
FileInfo srcInfo( src );
if ( !srcInfo.exists() )
return;
FileInfo dstInfo( dst );
if ( !dstInfo.exists() )
return;
if ( srcInfo.getFilepath() != srcInfo.getRealPath() )
srcInfo = FileInfo( srcInfo.getRealPath() );
if ( !dstInfo.isDirectory() ) {
dstInfo = FileInfo( dstInfo.getDirectoryPath() );
if ( dstInfo.getFilepath() != dstInfo.getRealPath() )
dstInfo = FileInfo( dstInfo.getRealPath() );
}
if ( srcInfo.getDirectoryPath() == dstInfo.getFilepath() )
return;
std::string srcPath( srcInfo.getFilepath() );
std::string dstPath( dstInfo.getFilepath() );
FileSystem::dirAddSlashAtEnd( dstPath );
dstPath += srcInfo.getFileName();
FileSystem::fileCopy( srcPath, dstPath );
}
std::string UITreeViewFS::getSelectionPath() const {
return static_cast<const FileSystemModel*>( getModel() )
->node( getSelection().first() )
.fullPath();
}
void UITreeViewFS::execute( const std::string& cmd ) {
auto cmdIt = mCommands.find( cmd );
if ( cmdIt != mCommands.end() )
return cmdIt->second();
}
} // namespace ecode

View File

@@ -1,5 +1,6 @@
#pragma once
#include <eepp/ui/keyboardshortcut.hpp>
#include <eepp/ui/uitreeview.hpp>
using namespace EE::UI;
@@ -16,12 +17,26 @@ class UITreeViewFS : public UITreeView {
Uint32 onMessage( const NodeMessage* msg ) override;
KeyBindings& getKeyBindings() { return mKeyBindings; }
void setSourceDrag( const std::string& src ) { mSrcDrag = src; }
void setSourceCopy( const std::string& src ) { mSrcCopy = src; }
void execute( const std::string& cmd );
protected:
KeyBindings mKeyBindings;
UnorderedMap<std::string, std::function<void()>> mCommands;
std::string mSrcDrag;
std::string mSrcCopy;
bool mWasCut{ false };
std::string getSelectionPath() const;
void moveFile( const std::string& src, const std::string& dst );
void copyFile( const std::string& src, const std::string& dst );
};
} // namespace ecode

View File

@@ -1,4 +1,5 @@
#include "uiwelcomescreen.hpp"
#include "customwidgets.hpp"
#include "ecode.hpp"
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/window/window.hpp>