diff --git a/buttleofx/MainWindow.qml b/buttleofx/MainWindow.qml index 93ff2ccf..019d9649 100644 --- a/buttleofx/MainWindow.qml +++ b/buttleofx/MainWindow.qml @@ -10,7 +10,7 @@ import QtQuick.LocalStorage 2.0 import "gui/graph/qml" import "gui/viewer/qml" import "gui/paramEditor/qml" -import "gui/browser_v2/qml" +import "gui/browser/qml" import "gui/plugin/qml" import "gui/shortcut/qml" import "gui/dialogs" @@ -880,6 +880,8 @@ ApplicationWindow { onItemClicked: isSupported ? browser.fileWindow.onItemClickedSlot(pathImg) : 0 onItemDoubleClicked: isSupported ? browser.fileWindow.onItemDoubleClickedSlot(absolutePath) : 0 } + + Component.onCompleted: browser.fileWindow.forceActiveFocus() } Item { diff --git a/buttleofx/gui/browser/__init__.py b/buttleofx/gui/browser/__init__.py index ad4aad32..e69de29b 100644 --- a/buttleofx/gui/browser/__init__.py +++ b/buttleofx/gui/browser/__init__.py @@ -1,2 +0,0 @@ -# flake8: noqa -from .fileModelBrowser import FileModelBrowser diff --git a/buttleofx/gui/browser_v2/actions/__init__.py b/buttleofx/gui/browser/actions/__init__.py similarity index 100% rename from buttleofx/gui/browser_v2/actions/__init__.py rename to buttleofx/gui/browser/actions/__init__.py diff --git a/buttleofx/gui/browser_v2/actions/actionInterface.py b/buttleofx/gui/browser/actions/actionInterface.py similarity index 75% rename from buttleofx/gui/browser_v2/actions/actionInterface.py rename to buttleofx/gui/browser/actions/actionInterface.py index 7ab054d2..0f4240fd 100644 --- a/buttleofx/gui/browser_v2/actions/actionInterface.py +++ b/buttleofx/gui/browser/actions/actionInterface.py @@ -15,12 +15,12 @@ def __init__(self, browserItem): self._browserItem = browserItem # required for abort processing - self._abortFlag = False # if action have been aborted - self._progress = 0.0 # progression of action between 0 and 1,will be used with Tuttle process in execute + self._abortFlag = False # if action has been aborted + self._progress = 0.0 # progression of action between 0 and 1 will be used with Tuttle process in execute + self._failed = False def __del__(self): logging.debug("Action destroyed") - pass def begin(self): if self._browserItem: @@ -34,11 +34,12 @@ def abort(self): """ Process revert() if action processed """ - if self._browserItem: - self._abortFlag = True - if self.isProcessed(): - self.revert() - self._browserItem.notifyRemoveAction() + if not self._browserItem or self._failed: + return + + self._abortFlag = True + if self.isProcessed(): + self.revert() def execute(self): raise NotImplementedError("ActionInterface::execute() must be implemented") @@ -49,10 +50,17 @@ def revert(self): def process(self): if self._abortFlag: return + self.begin() - self.execute() + + try: + self.execute() + except Exception as e: + print(str(e)) + self._failed = True + self.end() - self._progress = 1 + self._progress = 0 if self._failed else 1 self.progressChanged.emit() def getBrowserItem(self): diff --git a/buttleofx/gui/browser_v2/actions/actionManager.py b/buttleofx/gui/browser/actions/actionManager.py similarity index 98% rename from buttleofx/gui/browser_v2/actions/actionManager.py rename to buttleofx/gui/browser/actions/actionManager.py index cfe0f3c5..8533fe9d 100644 --- a/buttleofx/gui/browser_v2/actions/actionManager.py +++ b/buttleofx/gui/browser/actions/actionManager.py @@ -5,7 +5,7 @@ from quickmamba.models import QObjectListModel -from buttleofx.gui.browser_v2.actions.actionWorker import ActionWorker +from buttleofx.gui.browser.actions.actionWorker import ActionWorker class ActionManager(QtCore.QObject): diff --git a/buttleofx/gui/browser_v2/actions/actionWorker.py b/buttleofx/gui/browser/actions/actionWorker.py similarity index 100% rename from buttleofx/gui/browser_v2/actions/actionWorker.py rename to buttleofx/gui/browser/actions/actionWorker.py diff --git a/buttleofx/gui/browser_v2/actions/actionWrapper.py b/buttleofx/gui/browser/actions/actionWrapper.py similarity index 99% rename from buttleofx/gui/browser_v2/actions/actionWrapper.py rename to buttleofx/gui/browser/actions/actionWrapper.py index a3ea484d..44a870ea 100644 --- a/buttleofx/gui/browser_v2/actions/actionWrapper.py +++ b/buttleofx/gui/browser/actions/actionWrapper.py @@ -7,7 +7,6 @@ class ActionWrapper(QtCore.QObject): """ Expose useful data for qml such as nbActions and progression """ - # signals for qml nbProcessedChanged = QtCore.pyqtSignal() abortNotified = QtCore.pyqtSignal() progressChanged = QtCore.pyqtSignal() diff --git a/buttleofx/gui/browser_v2/actions/browserAction.py b/buttleofx/gui/browser/actions/browserAction.py similarity index 77% rename from buttleofx/gui/browser_v2/actions/browserAction.py rename to buttleofx/gui/browser/actions/browserAction.py index 4318d3c2..db0b9b56 100644 --- a/buttleofx/gui/browser_v2/actions/browserAction.py +++ b/buttleofx/gui/browser/actions/browserAction.py @@ -5,14 +5,13 @@ from pySequenceParser import sequenceParser -from buttleofx.gui.browser_v2.actions.actionManager import globalActionManager -from buttleofx.gui.browser_v2.actions.actionWrapper import ActionWrapper -from buttleofx.gui.browser_v2.browserItem import BrowserItem -from buttleofx.gui.browser_v2.actions.concreteActions.copy import Copy -from buttleofx.gui.browser_v2.actions.concreteActions.move import Move -from buttleofx.gui.browser_v2.actions.concreteActions.create import Create -from buttleofx.gui.browser_v2.actions.concreteActions.delete import Delete -from buttleofx.gui.browser_v2.browserModel import globalBrowserDialog, globalBrowser +from buttleofx.gui.browser.actions.actionManager import globalActionManager +from buttleofx.gui.browser.actions.actionWrapper import ActionWrapper +from buttleofx.gui.browser.browserItem import BrowserItem +from buttleofx.gui.browser.actions.concreteActions.copy import Copy +from buttleofx.gui.browser.actions.concreteActions.move import Move +from buttleofx.gui.browser.actions.concreteActions.create import Create +from buttleofx.gui.browser.actions.concreteActions.delete import Delete class BrowserAction(QtCore.QObject): @@ -40,11 +39,14 @@ def getCache(self): @QtCore.pyqtSlot(QtCore.QObject) def pushToActionManager(self, actionWrapper=None): + """ + Will process the action automatically by pushing to actionManager + """ if actionWrapper: globalActionManager.push(actionWrapper) - else: - if self._cacheActions: - globalActionManager.push(self._cacheActions) + return + if self._cacheActions: + globalActionManager.push(self._cacheActions) @QtCore.pyqtSlot() def handleCopy(self): @@ -75,6 +77,8 @@ def handlePaste(self, destination): for action in self._cacheActions.getActions(): action.setDestinationPath(destination) self.pushToActionManager() + self._cacheActions = None + self.cacheChanged.emit() @QtCore.pyqtSlot() def handleDelete(self): @@ -96,8 +100,4 @@ def handleNew(self, typeItem): self.pushToActionManager(ActionWrapper([Create(parent, new)])) # cache empty ? - isCache = QtCore.pyqtProperty(bool, isEmptyCache, notify=cacheChanged) - - -globalBrowserAction = BrowserAction(globalBrowser) -globalBrowserActionDialog = BrowserAction(globalBrowserDialog) \ No newline at end of file + isCache = QtCore.pyqtProperty(bool, isEmptyCache, notify=cacheChanged) \ No newline at end of file diff --git a/buttleofx/gui/browser_v2/__init__.py b/buttleofx/gui/browser/actions/concreteActions/__init__.py similarity index 100% rename from buttleofx/gui/browser_v2/__init__.py rename to buttleofx/gui/browser/actions/concreteActions/__init__.py diff --git a/buttleofx/gui/browser/actions/concreteActions/copy.py b/buttleofx/gui/browser/actions/concreteActions/copy.py new file mode 100644 index 00000000..9250e18e --- /dev/null +++ b/buttleofx/gui/browser/actions/concreteActions/copy.py @@ -0,0 +1,65 @@ +import os +import shutil +import os.path as op + +from buttleofx.gui.browser.actions.actionInterface import ActionInterface + + +class Copy(ActionInterface): + + def __init__(self, browserItem): + ActionInterface.__init__(self, browserItem) + self._srcPath = browserItem.getParentPath() + self._destPath = '' + self._framesPaths = [] # files sequences path copied + self._successCopy = True + + def setDestinationPath(self, newPath): + if not op.isdir(newPath): + raise TypeError + self._destPath = newPath.strip() + + def execute(self): + browserItem = self._browserItem + destPath = self._destPath + + # avoid recurse copy + if not op.exists(destPath) or browserItem.getPath() in destPath: + self._failed = True + print('%s already in %s: fail to copy' % (destPath, browserItem.getPath())) + return + + if browserItem.isFile(): + shutil.copy2(browserItem.getPath(), destPath) + + elif browserItem.isFolder(): + shutil.copytree(browserItem.getPath(), op.join(destPath, browserItem.getName())) + + elif browserItem.isSequence(): + seqParsed = browserItem.getSequence().getSequenceParsed() + frames = seqParsed.getFramesIterable() + + for f in frames: + filename = seqParsed.getFilenameAt(f) + filePath = op.join(browserItem.getParentPath(), filename) + try: + shutil.copy2(filePath, destPath) + self._framesPaths.append(op.join(destPath, filename)) + except Exception as e: + print(str(e)) + pass + + def revert(self): + browserItem = self.getBrowserItem() + copiedPath = op.join(self._destPath, browserItem.getName()) + + if browserItem.isFile() and op.exists(copiedPath): + os.remove(copiedPath) + + if browserItem.isFolder() and op.exists(copiedPath): + shutil.rmtree(copiedPath) + + if browserItem.isSequence(): + for f in self._framesPaths: + if op.exists(f): + os.remove(f) diff --git a/buttleofx/gui/browser_v2/actions/concreteActions/create.py b/buttleofx/gui/browser/actions/concreteActions/create.py similarity index 92% rename from buttleofx/gui/browser_v2/actions/concreteActions/create.py rename to buttleofx/gui/browser/actions/concreteActions/create.py index ddf06741..9bbec164 100644 --- a/buttleofx/gui/browser_v2/actions/concreteActions/create.py +++ b/buttleofx/gui/browser/actions/concreteActions/create.py @@ -2,7 +2,7 @@ import shutil import logging -from buttleofx.gui.browser_v2.actions.actionInterface import ActionInterface +from buttleofx.gui.browser.actions.actionInterface import ActionInterface class Create(ActionInterface): @@ -17,7 +17,7 @@ def __init__(self, parentBrowserItem, newBrowserItem): def execute(self): # TODO: Check parent's permission in try catch - + # TODO: find elegant way to find next: sort, find next if match then extract version number parent = self.getBrowserItem() newBrowserItem = self._newBrowserItem parentPath = parent.getPath() diff --git a/buttleofx/gui/browser_v2/actions/concreteActions/delete.py b/buttleofx/gui/browser/actions/concreteActions/delete.py similarity index 96% rename from buttleofx/gui/browser_v2/actions/concreteActions/delete.py rename to buttleofx/gui/browser/actions/concreteActions/delete.py index 20dc8494..d3336043 100644 --- a/buttleofx/gui/browser_v2/actions/concreteActions/delete.py +++ b/buttleofx/gui/browser/actions/concreteActions/delete.py @@ -1,7 +1,7 @@ import os import shutil import tempfile -from buttleofx.gui.browser_v2.actions.actionInterface import ActionInterface +from buttleofx.gui.browser.actions.actionInterface import ActionInterface class Delete(ActionInterface): diff --git a/buttleofx/gui/browser_v2/actions/concreteActions/move.py b/buttleofx/gui/browser/actions/concreteActions/move.py similarity index 57% rename from buttleofx/gui/browser_v2/actions/concreteActions/move.py rename to buttleofx/gui/browser/actions/concreteActions/move.py index 9ec62c26..6cf5fee5 100644 --- a/buttleofx/gui/browser_v2/actions/concreteActions/move.py +++ b/buttleofx/gui/browser/actions/concreteActions/move.py @@ -1,38 +1,47 @@ import os import shutil +import os.path as op -from buttleofx.gui.browser_v2.actions.actionInterface import ActionInterface +from buttleofx.gui.browser.actions.actionInterface import ActionInterface class Move(ActionInterface): def __init__(self, browserItem): - # destination must be a directory - # if not destination.isFolder(): - # raise TypeError ActionInterface.__init__(self, browserItem) self._srcPath = browserItem.getParentPath() - self._destPath = "" + self._destPath = '' def setDestinationPath(self, newPath): + if not op.isdir(newPath): + raise TypeError self._destPath = newPath.strip() def execute(self): browserItem = self._browserItem destinationPath = self._destPath + copyPath = op.join(destinationPath, browserItem.getName()) + + if browserItem.getPath() in destinationPath or op.exists(copyPath): + self._failed = True + return # Move file, folder, and sequence # TODO: Check destination permission in try catch if browserItem.isFile() or browserItem.isFolder(): - if os.path.exists(destinationPath): + if op.exists(destinationPath): shutil.move(browserItem.getPath(), destinationPath) def revert(self): browserItem = self._browserItem srcPath = self._srcPath destPath = self._destPath - destItemPath = os.path.join(destPath, browserItem.getName()) + destItemPath = op.join(destPath, browserItem.getName()) + + if op.exists(op.join(srcPath, op.basename(destItemPath))): + self._failed = True + return if browserItem.isFile() or browserItem.isFolder(): - if os.path.exists(srcPath): + if op.exists(srcPath): shutil.move(destItemPath, srcPath) diff --git a/buttleofx/gui/browser_v2/actions/concreteActions/rename.py b/buttleofx/gui/browser/actions/concreteActions/rename.py similarity index 97% rename from buttleofx/gui/browser_v2/actions/concreteActions/rename.py rename to buttleofx/gui/browser/actions/concreteActions/rename.py index a5b76ae1..70da2de8 100644 --- a/buttleofx/gui/browser_v2/actions/concreteActions/rename.py +++ b/buttleofx/gui/browser/actions/concreteActions/rename.py @@ -1,5 +1,5 @@ import os -from buttleofx.gui.browser_v2.actions.actionInterface import ActionInterface +from buttleofx.gui.browser.actions.actionInterface import ActionInterface class Rename(ActionInterface): diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/helper/__init__.py b/buttleofx/gui/browser/actions/testConcreteActions/helper/__init__.py similarity index 100% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/helper/__init__.py rename to buttleofx/gui/browser/actions/testConcreteActions/helper/__init__.py diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/helper/file.py b/buttleofx/gui/browser/actions/testConcreteActions/helper/file.py similarity index 100% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/helper/file.py rename to buttleofx/gui/browser/actions/testConcreteActions/helper/file.py diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/helper/helper.py b/buttleofx/gui/browser/actions/testConcreteActions/helper/helper.py similarity index 100% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/helper/helper.py rename to buttleofx/gui/browser/actions/testConcreteActions/helper/helper.py diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/helper/sequence.py b/buttleofx/gui/browser/actions/testConcreteActions/helper/sequence.py similarity index 100% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/helper/sequence.py rename to buttleofx/gui/browser/actions/testConcreteActions/helper/sequence.py diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/readme.md b/buttleofx/gui/browser/actions/testConcreteActions/readme.md similarity index 57% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/readme.md rename to buttleofx/gui/browser/actions/testConcreteActions/readme.md index 308bc88d..1754be1a 100644 --- a/buttleofx/gui/browser_v2/actions/testConcreteActions/readme.md +++ b/buttleofx/gui/browser/actions/testConcreteActions/readme.md @@ -1,3 +1,3 @@ # How to run test -$PYTHONHOME/bin/python -m unittest discover -v $BUTTLE_TOP_DIR/ButtleOFX/buttleofx/gui/browser_v2/actions/testConcreteActions +$PYTHONHOME/bin/python -m unittest discover -v $BUTTLE_TOP_DIR/ButtleOFX/buttleofx/gui/browser/actions/testConcreteActions diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_copy.py b/buttleofx/gui/browser/actions/testConcreteActions/test_copy.py similarity index 97% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/test_copy.py rename to buttleofx/gui/browser/actions/testConcreteActions/test_copy.py index dcfef790..6d23e73a 100644 --- a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_copy.py +++ b/buttleofx/gui/browser/actions/testConcreteActions/test_copy.py @@ -4,10 +4,10 @@ # from OpenGL import GL -from buttleofx.gui.browser_v2.browserItem import BrowserItem -from buttleofx.gui.browser_v2.actions.concreteActions.copy import Copy +from buttleofx.gui.browser.browserItem import BrowserItem +from buttleofx.gui.browser.actions.concreteActions.copy import Copy from pySequenceParser import sequenceParser -import buttleofx.gui.browser_v2.actions.testConcreteActions.helper as h +import buttleofx.gui.browser.actions.testConcreteActions.helper as h class TestCopy(unittest.TestCase): diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_create.py b/buttleofx/gui/browser/actions/testConcreteActions/test_create.py similarity index 97% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/test_create.py rename to buttleofx/gui/browser/actions/testConcreteActions/test_create.py index f2ffceef..177930d5 100644 --- a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_create.py +++ b/buttleofx/gui/browser/actions/testConcreteActions/test_create.py @@ -2,8 +2,8 @@ import tempfile import os -from buttleofx.gui.browser_v2.browserItem import BrowserItem -from buttleofx.gui.browser_v2.actions.concreteActions.create import Create +from buttleofx.gui.browser.browserItem import BrowserItem +from buttleofx.gui.browser.actions.concreteActions.create import Create from pySequenceParser import sequenceParser diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_delete.py b/buttleofx/gui/browser/actions/testConcreteActions/test_delete.py similarity index 96% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/test_delete.py rename to buttleofx/gui/browser/actions/testConcreteActions/test_delete.py index 43dbb9e3..4d2d7b06 100644 --- a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_delete.py +++ b/buttleofx/gui/browser/actions/testConcreteActions/test_delete.py @@ -5,10 +5,10 @@ # from OpenGL import GL -from buttleofx.gui.browser_v2.browserItem import BrowserItem -from buttleofx.gui.browser_v2.actions.concreteActions.delete import Delete +from buttleofx.gui.browser.browserItem import BrowserItem +from buttleofx.gui.browser.actions.concreteActions.delete import Delete from pySequenceParser import sequenceParser -import buttleofx.gui.browser_v2.actions.testConcreteActions.helper as h +import buttleofx.gui.browser.actions.testConcreteActions.helper as h class TestDelete(unittest.TestCase): diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_move.py b/buttleofx/gui/browser/actions/testConcreteActions/test_move.py similarity index 98% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/test_move.py rename to buttleofx/gui/browser/actions/testConcreteActions/test_move.py index ccbfd0ad..7667b067 100644 --- a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_move.py +++ b/buttleofx/gui/browser/actions/testConcreteActions/test_move.py @@ -4,8 +4,8 @@ # from OpenGL import GL -from buttleofx.gui.browser_v2.browserItem import BrowserItem -from buttleofx.gui.browser_v2.actions.concreteActions.move import Move +from buttleofx.gui.browser.browserItem import BrowserItem +from buttleofx.gui.browser.actions.concreteActions.move import Move from pySequenceParser import sequenceParser diff --git a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_rename.py b/buttleofx/gui/browser/actions/testConcreteActions/test_rename.py similarity index 97% rename from buttleofx/gui/browser_v2/actions/testConcreteActions/test_rename.py rename to buttleofx/gui/browser/actions/testConcreteActions/test_rename.py index 134ccca3..8bd02f73 100644 --- a/buttleofx/gui/browser_v2/actions/testConcreteActions/test_rename.py +++ b/buttleofx/gui/browser/actions/testConcreteActions/test_rename.py @@ -4,10 +4,10 @@ # from OpenGL import GL -from buttleofx.gui.browser_v2.browserItem import BrowserItem -from buttleofx.gui.browser_v2.actions.concreteActions.rename import Rename +from buttleofx.gui.browser.browserItem import BrowserItem +from buttleofx.gui.browser.actions.concreteActions.rename import Rename from pySequenceParser import sequenceParser -import buttleofx.gui.browser_v2.actions.testConcreteActions.helper as h +import buttleofx.gui.browser.actions.testConcreteActions.helper as h class TestRename(unittest.TestCase): diff --git a/buttleofx/gui/browser_v2/browserItem.py b/buttleofx/gui/browser/browserItem.py similarity index 97% rename from buttleofx/gui/browser_v2/browserItem.py rename to buttleofx/gui/browser/browserItem.py index 755cf6fe..38be76eb 100644 --- a/buttleofx/gui/browser_v2/browserItem.py +++ b/buttleofx/gui/browser/browserItem.py @@ -6,14 +6,14 @@ from multiprocessing import Process, ProcessError from threading import Lock -from PyQt5 import QtCore +from PyQt5 import QtCore, QtGui from pySequenceParser import sequenceParser from pyTuttle import tuttle -from buttleofx.gui.browser_v2.sequenceWrapper import SequenceWrapper -from buttleofx.gui.browser_v2.thumbnailUtil import ThumbnailUtil, thumbnailPool +from buttleofx.gui.browser.sequenceWrapper import SequenceWrapper +from buttleofx.gui.browser.thumbnailUtil import ThumbnailUtil, thumbnailPool class ItemType(object): @@ -332,6 +332,12 @@ def buildThumbnailProcess(self, imgPath): except Exception as e: logging.debug(str(e)) + @QtCore.pyqtSlot(result=bool) + def launchDefaultApplication(self): + if not self._path: + raise ValueError + return QtGui.QDesktopServices.openUrl(QtCore.QUrl(self._path)) + # ################################### Data exposed to QML #################################### # isSelected = QtCore.pyqtProperty(bool, getSelected, setSelected, notify=selectedChanged) diff --git a/buttleofx/gui/browser_v2/browserModel.py b/buttleofx/gui/browser/browserModel.py similarity index 89% rename from buttleofx/gui/browser_v2/browserModel.py rename to buttleofx/gui/browser/browserModel.py index 9fda596f..b4dfd4ed 100644 --- a/buttleofx/gui/browser_v2/browserModel.py +++ b/buttleofx/gui/browser/browserModel.py @@ -11,10 +11,10 @@ from quickmamba.models import QObjectListModel -from buttleofx.gui.browser_v2.browserItem import BrowserItem, ItemType -from buttleofx.gui.browser_v2.browserSortOn import SortOn -from buttleofx.gui.browser_v2.parallelThread import ParallelThread, WithMutex, WithBool -from buttleofx.gui.browser_v2.actions.actionManager import globalActionManager +from buttleofx.gui.browser.browserItem import BrowserItem, ItemType +from buttleofx.gui.browser.browserSortOn import SortOn +from buttleofx.gui.browser.parallelThread import ParallelThread, WithMutex, WithBool +from buttleofx.gui.browser.actions.actionManager import globalActionManager class BrowserModel(QtCore.QObject): @@ -35,26 +35,33 @@ class BrowserModel(QtCore.QObject): loadingChanged = QtCore.pyqtSignal() def __init__(self, path=op.expanduser("~/"), sync=False, showSeq=True, hideDotFiles=True, filterFiles="*", - parent=None, buildThumbnail=True): + parent=None, buildThumbnail=True, watchCurrentDir=False): """ Engine of browser user interaction with browserUI :param parent: Qt parent """ QtCore.QObject.__init__(self, parent) self._currentPath = path - self._bufferBrowserItems = [] # used when recursive process: fix async signal connection when add object - self._browserItems = [] # used only in python side - self._browserItemsModel = QObjectListModel(self) # used for UI + self._buildThumbnails = buildThumbnail + self._isSync = sync + self._watchCurrentDir = watchCurrentDir self._filter = filterFiles self._hideDotFiles = hideDotFiles self._showSeq = showSeq self._sortOn = SortOn() + + self._bufferBrowserItems = [] # used when recursive process: fix async signal connection when add object + self._browserItems = [] # used only in python side + self._cacheSelected = [] # contains paths + + self._browserItemsModel = QObjectListModel(self) # used for UI + self._listFolderNavBar = QObjectListModel(self) + self._fileWatcher = QtCore.QFileSystemWatcher(self) if watchCurrentDir else None + self._actionManager = globalActionManager # used to check if item already exists in actions list self._parallelThread = ParallelThread() self._isLoading = WithBool(False, self.loadingChanged) - self._buildThumbnails = buildThumbnail - self._listFolderNavBar = QObjectListModel(self) - self._isSync = sync + self.initSlotConnection(sync) def initSlotConnection(self, isSync): @@ -69,11 +76,15 @@ def initSlotConnection(self, isSync): self.addItemSync.connect(self.onAddItemSync, typeConnection) self.clearItemsSync.connect(self.onClearItemsSync, typeConnection) + if self._fileWatcher: + self._fileWatcher.directoryChanged.connect(lambda: self.load()) + def getParallelThread(self): return self._parallelThread @QtCore.pyqtSlot(str) - def loadData(self, recursivePattern=''): + def load(self, recursivePattern=''): + self.attachWatcher(self._currentPath) if self._isSync: self.updateItems(recursivePattern) else: @@ -152,6 +163,7 @@ def pushBrowserItems(self, allItems, toModel=True): @QtCore.pyqtSlot(object, bool) def onAddItemSync(self, bItem, toModel=True): + bItem.setSelected(bItem.getPath() in self._cacheSelected) self._browserItems.append(bItem) self.sortBrowserItems.emit() if toModel: @@ -200,7 +212,7 @@ def searchRecursively(self, pattern, modelRequester): # Do not compute thumbnails on all elements, but only manually on matching files. recursiveModel = BrowserModel(bItem.getPath(), True, self._showSeq, self._hideDotFiles, self._filter, buildThumbnail=False) # Browse items for this folder and fill model - recursiveModel.loadData() + recursiveModel.load() # Launch a search on all sub directories recursiveModel.searchRecursively(pattern.lower(), modelRequester) @@ -209,12 +221,26 @@ def getFilter(self): def setFilter(self, newFilter): self._filter = newFilter - self.loadData() + self.load() self.filterChanged.emit() def getCurrentPath(self): return self._currentPath + def attachWatcher(self, path): + """ + Auto refresh: change the watched path + """ + if not self._watchCurrentDir: + return + if self._fileWatcher.directories(): + self._fileWatcher.removePaths(self._fileWatcher.directories()) + + if not op.exists(path): + # don't attach watcher if path doesn't exist + return + self._fileWatcher.addPath(path) + @QtCore.pyqtSlot(str) def setCurrentPath(self, newCurrentPath): newCurrentPath = newCurrentPath.strip() @@ -223,7 +249,7 @@ def setCurrentPath(self, newCurrentPath): logging.debug('browserModel.setCurrentPath("%s")', newCurrentPath) self._currentPath = newCurrentPath - self.loadData() + self.load() self.refresh_listFolderNavbar() self.currentPathChanged.emit() @@ -242,7 +268,7 @@ def isHiddenDotFiles(self): def setHideDotFiles(self, hide): self._hideDotFiles = hide - self.loadData() + self.load() self.hideDotFilesChanged.emit() def getFieldToSort(self): @@ -259,9 +285,6 @@ def onSortBrowserItems(self): elif self._sortOn.getFieldToSort() == SortOn.onSize: self._browserItems.sort(key=lambda it: (it.getType(), it.getWeight()), reverse=rev) - # sort by folder, file - self._browserItems.sort(key=lambda it: (it.getType())) # TODO: check needed - def searchIndexItem(self, bItem): """ :param bItem: BrowserItem @@ -279,13 +302,13 @@ def setFieldToSort(self, newField, reverse=False): self._sortOn.setFieldToSort(newField, reverse) self.onSortBrowserItems() self.sortOnChanged.emit() - self.loadData() + self.load() @QtCore.pyqtSlot(bool) def setShowSequence(self, seqBool): self._showSeq = seqBool self.filterChanged.emit() - self.loadData() + self.load() @QtCore.pyqtSlot(result=bool) def isShownSequence(self): @@ -311,7 +334,7 @@ def getItems(self): @QtCore.pyqtSlot(result=str) def getParentPath(self): - parent = op.dirname(self.currentPath.rstrip('/')) + parent = op.dirname(self._currentPath.rstrip('/')) return parent + ("/" if not parent else '') @QtCore.pyqtSlot(result=QObjectListModel) @@ -375,15 +398,19 @@ def getSelectedItems(self): @QtCore.pyqtSlot() def refresh(self): - self.loadData() + self.load() @QtCore.pyqtSlot() def unselectAllItems(self): + self._cacheSelected.clear() for bItem in self._browserItems: bItem.setSelected(False) @QtCore.pyqtSlot() def selectAllItems(self): + self._cacheSelected.clear() + self._cacheSelected = [f.getPath() for f in self._browserItems] + for bItem in self._browserItems: bItem.setSelected(True) @@ -391,7 +418,9 @@ def selectAllItems(self): def selectItem(self, index): self.unselectAllItems() if index in range(len(self._browserItems)): - self._browserItems[index].setSelected(True) + bItem = self._browserItems[index] + self._cacheSelected = [bItem.getPath()] + bItem.setSelected(True) @QtCore.pyqtSlot(int) def selectItemTo(self, index): @@ -408,7 +437,9 @@ def selectItemTo(self, index): firstSelected, index = index, firstSelected for i in range(firstSelected, index+1): - self._browserItems[i].setSelected(True) + bItem = self._browserItems[i] + bItem.setSelected(True) + self._cacheSelected.append(bItem.getPath()) # ############################################# Data exposed to QML ############################################# # @@ -424,8 +455,4 @@ def selectItemTo(self, index): listFolderNavBar = QtCore.pyqtProperty(QtCore.QObject, getListFolderNavBar, notify=currentPathChanged) sortOn = QtCore.pyqtProperty(str, getFieldToSort, notify=sortOnChanged) selectedItems = QtCore.pyqtProperty(QObjectListModel, getSelectedItems, notify=modelChanged) - loading = QtCore.pyqtProperty(bool, isLoading, notify=loadingChanged) - - -globalBrowser = BrowserModel() -globalBrowserDialog = BrowserModel() \ No newline at end of file + loading = QtCore.pyqtProperty(bool, isLoading, notify=loadingChanged) \ No newline at end of file diff --git a/buttleofx/gui/browser_v2/browserSortOn.py b/buttleofx/gui/browser/browserSortOn.py similarity index 100% rename from buttleofx/gui/browser_v2/browserSortOn.py rename to buttleofx/gui/browser/browserSortOn.py diff --git a/buttleofx/gui/browser_v2/browserTree.py b/buttleofx/gui/browser/browserTree.py similarity index 70% rename from buttleofx/gui/browser_v2/browserTree.py rename to buttleofx/gui/browser/browserTree.py index 85538a8a..ee2bcf01 100644 --- a/buttleofx/gui/browser_v2/browserTree.py +++ b/buttleofx/gui/browser/browserTree.py @@ -1,5 +1,5 @@ from PyQt5 import QtCore -from buttleofx.gui.browser_v2.browserModel import BrowserModel +from buttleofx.gui.browser.browserModel import BrowserModel class BrowserTree(QtCore.QObject): diff --git a/buttleofx/gui/browser/fileModelBrowser.py b/buttleofx/gui/browser/fileModelBrowser.py deleted file mode 100644 index 3644a3bc..00000000 --- a/buttleofx/gui/browser/fileModelBrowser.py +++ /dev/null @@ -1,389 +0,0 @@ -import os -import logging - -from pyTuttle import tuttle - -from PyQt5 import QtCore -from PyQt5 import QtQuick - -from quickmamba.patterns import Singleton -from pySequenceParser import sequenceParser -from .sequenceWrapper import SequenceWrapper - -from quickmamba.models import QObjectListModel - - -class FileItem(QtCore.QObject): - - _isSelected = False - - class Type(): - """ Enum """ - File = 'File' - Folder = 'Folder' - Sequence = 'Sequence' - - def __init__(self, folder, fileName, fileType, seq, supported): - super(FileItem, self).__init__() - - self._filepath = os.path.join(folder, fileName) - self._fileType = fileType - self._isSupported = supported - - if fileType == FileItem.Type.File: - if supported: - self._fileImg = 'image://buttleofx/' + self._filepath - else: - self._fileImg = "../../img/buttons/browser/file-icon.png" - - try: - # May throw exception on bad symlink - self._fileWeight = os.stat(self._filepath).st_size - except FileNotFoundError: - self._fileWeight = 0 - - self._fileExtension = os.path.splitext(fileName)[1] - self._seq = None - - elif fileType == FileItem.Type.Folder: - self._fileImg = "../../img/buttons/browser/folder-icon.png" - self._seq = None - self._fileWeight = 0.0 - self._fileExtension = "" - - elif fileType == FileItem.Type.Sequence: - time = int(seq.getFirstTime() + (seq.getLastTime() - seq.getFirstTime()) * 0.5) - seqPath = seq.getAbsoluteFilenameAt(time) - - if not os.path.exists(seqPath): - time = seq.getFirstTime() - seqPath = seq.getAbsoluteFilenameAt(time) - - seqPath = seq.getAbsoluteStandardPattern() - - if supported: - self._fileImg = 'image://buttleofx/' + seqPath - else: - self._fileImg = "../../img/buttons/browser/file-icon.png" - - self._seq = SequenceWrapper(seq) - self._fileWeight = self._seq.getWeight() - (_, extension) = os.path.splitext(seqPath) - self._fileExtension = extension - - # ############################################ Methods exposed to QML ############################################ # - - @QtCore.pyqtSlot(result=str) - def getFilepath(self): - return self._filepath - - @QtCore.pyqtSlot(result=str) - def getFileType(self): - return self._fileType - - @QtCore.pyqtSlot(result=QtCore.QSizeF) - def getImageSize(self): - g = tuttle.Graph() - node = g.createNode(tuttle.getReaders(self._fileExtension)[0], self._fileImg).asImageEffectNode() - g.setup() - timeMin = self.getFileTime().min - g.setupAtTime(timeMin) - rod = node.getRegionOfDefinition(timeMin) - width = rod.x2 - rod.x1 - height = rod.y2 - rod.y1 - return QtCore.QSizeF(width, height) - - @QtCore.pyqtSlot(result=bool) - def getSupported(self): - return self._isSupported - - # ######################################## Methods private to this class ####################################### # - - # ## Getters ## # - - def getFileImg(self): - return self._fileImg - - def getFileName(self): - return os.path.basename(self._filepath) - - def getFileWeight(self): - return self._fileWeight - - def getFileTime(self): - g = tuttle.Graph() - node = g.createNode(tuttle.getReaders(self._fileExtension)[0], self._filepath).asImageEffectNode() - g.setup() - time = node.getTimeDomain() - return time - - def getSelected(self): - return self._isSelected - - def getSequence(self): - return self._seq - - # ## Setters ## # - - def setFileName(self, newName): - os.rename(self.filepath, os.path.join(os.path.dirname(self._filepath), newName)) - - def setFilepath(self, newpath): - import shutil - shutil.move(self.filepath, os.path.join(newpath, self.fileName)) - - def setSelected(self, isSelected): - self._isSelected = isSelected - self.isSelectedChange.emit() - - # ############################################# Data exposed to QML ############################################## # - - filepath = QtCore.pyqtProperty(str, getFilepath, setFilepath, constant=True) - fileType = QtCore.pyqtProperty(str, getFileType, constant=True) - fileName = QtCore.pyqtProperty(str, getFileName, setFileName, constant=True) - fileWeight = QtCore.pyqtProperty(float, getFileWeight, constant=True) - - imageSize = QtCore.pyqtProperty(QtCore.QSize, getImageSize, constant=True) - isSelectedChange = QtCore.pyqtSignal() - isSelected = QtCore.pyqtProperty(bool, getSelected, setSelected, notify=isSelectedChange) - fileImg = QtCore.pyqtProperty(str, getFileImg, constant=True) - seq = QtCore.pyqtProperty(QtCore.QObject, getSequence, constant=True) - - -class FileModelBrowser(QtQuick.QQuickItem): - """Class FileModelBrowser""" - - _folder = "" - _firstFolder = "" - _fileItems = [] - _fileItemsModel = None - _nameFilter = "*" - - def __init__(self, parent=None): - super(FileModelBrowser, self).__init__(parent) - self._fileItemsModel = QObjectListModel(self) - self._showSeq = False - - # ############################################ Methods exposed to QML ############################################ # - - # ## Getters ## # - - @QtCore.pyqtSlot(str, result=QtCore.QObject) - def getFilteredFileItems(self, fileFilter): - suggestions = [] - - try: - _, dirs, _ = next(os.walk(os.path.dirname(fileFilter))) - dirs = sorted(dirs, key=lambda v: v.upper()) - - for d in dirs: - if d.startswith("."): - # Ignore hidden files by default - # TODO: need an option for that - continue - if d.startswith(os.path.basename(fileFilter)): - suggestions.append(FileItem(os.path.dirname(fileFilter), d, FileItem.Type.Folder, "", True)) - - except Exception: - pass - suggestions.sort(key=lambda fileItem: fileItem.fileName.lower()) - - suggestionsQt = QObjectListModel(self) - suggestionsQt.setObjectList(suggestions) - return suggestionsQt - - @QtCore.pyqtSlot(result=QtCore.QObject) - def getFileItems(self): - return self._fileItemsModel - - @QtCore.pyqtSlot(result=str) - def getFirstFolder(self): - return self._firstFolder - - @QtCore.pyqtSlot(result=QtCore.QObject) - def getLastSelected(self): - for item in reversed(self._fileItems): - if item.isSelected: - return item - return None - - @QtCore.pyqtSlot(result=QtCore.QObject) - def getSelectedItems(self): - selectedList = QObjectListModel(self) - for item in self._fileItems: - if item.isSelected: - selectedList.append(item) - return selectedList - - # ## Setters ## # - - @QtCore.pyqtSlot(str) - def setFirstFolder(self, firstFolder): - self._firstFolder = firstFolder - - # ## Others ## # - - @QtCore.pyqtSlot(str, int) - def changeFileName(self, newName, index): - if index < len(self._fileItems): - self._fileItems[index].fileName = newName - self.updateFileItems(self._folder) - - @QtCore.pyqtSlot(str) - def createFolder(self, path): - os.mkdir(path) - self.updateFileItems(self._folder) - - @QtCore.pyqtSlot(int) - def deleteItem(self, index): - if index < len(self._fileItems): - if self._fileItems[index].fileType == FileItem.Type.Folder: - import shutil - shutil.rmtree(self._fileItems[index].filepath) - if self._fileItems[index].fileType == FileItem.Type.File: - os.remove(self._fileItems[index].filepath) - self.updateFileItems(self._folder) - - @QtCore.pyqtSlot(result=bool) - def isEmpty(self): - return not self._fileItems - - @QtCore.pyqtSlot(int, str) - def moveItem(self, index, newpath): - if index < len(self._fileItems): - self._fileItems[index].filepath = newpath - self.updateFileItems(self._folder) - - @QtCore.pyqtSlot(int) - def selectItem(self, index): - for item in self._fileItems: - item.isSelected = False - if index < len(self._fileItems): - self._fileItems[index].isSelected = True - - @QtCore.pyqtSlot(int) - def selectItems(self, index): - if index < len(self._fileItems): - self._fileItems[index].isSelected = True - - @QtCore.pyqtSlot(int, int) - def selectItemsByShift(self, begin, end): - if begin > end: - tmp = begin - begin = end - end = tmp - for i in range(begin, end + 1): - if i < len(self._fileItems): - self._fileItems[i].isSelected = True - - @QtCore.pyqtSlot(str) - def updateFileItems(self, folder): - logging.debug('updateFileItems: %s' % folder) - if not folder: - return - - self._fileItems = [] - self._fileItemsModel.clear() - allDirs = [] - allFiles = [] - allSeqs = [] - - items = sequenceParser.browse(folder) - dirs = [item.getFilename() for item in items if item.getType() == sequenceParser.eTypeFolder] - seqs = [item.getSequence() for item in items if item.getType() == sequenceParser.eTypeSequence] - files = [item.getFilename() for item in items if item.getType() == sequenceParser.eTypeFile] - - for d in dirs: - if d.startswith("."): - # Ignore hidden files by default - # TODO: need an option for that - continue - allDirs.append(FileItem(folder, d, FileItem.Type.Folder, "", True)) - - if self._showSeq: - for s in seqs: - sPath = s.getStandardPattern() - if sPath.startswith("."): - # Ignore hidden files by default - # TODO: need an option for that - continue - readers = tuttle.getReaders(sPath) - logging.debug('SEQ readers: %s' % readers) - supported = bool(readers) - if not supported and self._nameFilter != "*": - continue - allSeqs.append(FileItem(folder, sPath, FileItem.Type.Sequence, s, supported)) - - for f in files: - if f.startswith("."): - # Ignore hidden files by default - # TODO: need an option for that - continue - readers = tuttle.getReaders(f) - logging.debug('FILE readers: %s' % readers) - supported = bool(readers) - if not supported and self._nameFilter != "*": - continue - allFiles.append(FileItem(folder, f, FileItem.Type.File, "", supported)) - - allDirs.sort(key=lambda fileItem: fileItem.fileName.lower()) - allFiles.sort(key=lambda fileItem: fileItem.fileName.lower()) - allSeqs.sort(key=lambda fileItem: fileItem.fileName.lower()) - self._fileItems = allDirs + allFiles + allSeqs - - self._fileItemsModel.setObjectList(self._fileItems) - - # ######################################## Methods private to this class ######################################## # - - # ## Getters ## # - - def getFilter(self): - return self._nameFilter - - def getFolder(self): - return self._folder - - def getFolderExists(self): - return os.path.exists(self._folder) - - def getParentFolder(self): - return os.path.dirname(self._folder) - - def getShowSeq(self): - return self._showSeq - - def getSize(self): - return len(self._fileItems) - 1 - - # ## Setters ## # - - def setFilter(self, nameFilter): - self._nameFilter = nameFilter - self.updateFileItems(self._folder) - self.nameFilterChange.emit() - - def setFolder(self, folder): - logging.debug('fileModelBrowser.setFolder("%s")' % folder) - self._folder = folder - self.updateFileItems(folder) - self.folderChanged.emit() - - def setShowSeq(self, checkSeq): - self._showSeq = checkSeq - self.updateFileItems(self._folder) - self.showSeqChanged.emit() - - # ############################################# Data exposed to QML ############################################# # - - folderChanged = QtCore.pyqtSignal() - folder = QtCore.pyqtProperty(str, getFolder, setFolder, notify=folderChanged) - exists = QtCore.pyqtProperty(bool, getFolderExists, notify=folderChanged) - parentFolder = QtCore.pyqtProperty(str, getParentFolder, constant=True) - - fileItems = QtCore.pyqtProperty(QtCore.QObject, getFileItems, notify=folderChanged) - nameFilterChange = QtCore.pyqtSignal() - nameFilter = QtCore.pyqtProperty(str, getFilter, setFilter, notify=nameFilterChange) - size = QtCore.pyqtProperty(int, getSize, constant=True) - showSeqChanged = QtCore.pyqtSignal() - showSeq = QtCore.pyqtProperty(bool, getShowSeq, setShowSeq, notify=showSeqChanged) - diff --git a/buttleofx/gui/browser/main.py b/buttleofx/gui/browser/main.py index cd7350b7..13c588c3 100644 --- a/buttleofx/gui/browser/main.py +++ b/buttleofx/gui/browser/main.py @@ -1,24 +1,46 @@ -import os -import sys - -from PyQt5 import QtCore -from PyQt5 import QtWidgets -from PyQt5 import QtQuick - - -currentFilePath = os.path.dirname(os.path.abspath(__file__)) -quickmambaPath = os.path.join(currentFilePath, '../../../QuickMamba') -sys.path.append(quickmambaPath) - -if __name__ == '__main__': - app = QtWidgets.QApplication(sys.argv) - view = QtQuick.QQuickView() - - rc = view.rootContext() - - # view.setWindowTitle("Browser") - view.setSource(QtCore.QUrl(os.path.join(currentFilePath, "qml/Browser.qml"))) - view.setResizeMode(QtQuick.QQuickView.SizeRootObjectToView) - - view.show() - app.exec_() +#! /usr/bin/env python3 + +''' + # How to run browser in standalone mode ? + copy/paste those lines to the run_buttleofx.sh + + ## Add QML2 env. var. to run browser + export QML2_IMPORT_PATH=$QT_DIR/qml + + ## Run browser in standalone mode (comment original run) + $PYTHONHOME/bin/python $BUTTLE_TOP_DIR/ButtleOFX/buttleofx/gui/browser/main.py + +''' + +import os +import sys +from PyQt5.QtQml import qmlRegisterType +from buttleofx.gui.browser.browserModel import BrowserModel +from pyTuttle import tuttle +from buttleofx.gui.browser.standaloneUtils import ImageProvider +from PyQt5 import QtWidgets, QtQml, QtCore, QtQuick +# To prevent drivers conflicts between Mesa-utils and NVIDIA drivers on Ubuntu +from OpenGL import GL + +currentFilePath = os.path.dirname(os.path.abspath(__file__)) + +if __name__ == '__main__': + + tuttle.core().preload() + + app = QtWidgets.QApplication(sys.argv) + engine = QtQml.QQmlEngine(app) + engine.quit.connect(app.quit) + engine.addImageProvider("buttleofx", ImageProvider()) + + view = QtQuick.QQuickView(engine, None) + rc = view.rootContext() + + qmlRegisterType(BrowserModel, 'BrowserModel', 1, 0, 'BrowserModel') + qmlFilePath = os.path.join(currentFilePath, "qml/Browser.qml") + + view.setSource(QtCore.QUrl(qmlFilePath)) + view.setResizeMode(QtQuick.QQuickView.SizeRootObjectToView) + + view.show() + app.exec_() diff --git a/buttleofx/gui/browser_v2/parallelThread.py b/buttleofx/gui/browser/parallelThread.py similarity index 100% rename from buttleofx/gui/browser_v2/parallelThread.py rename to buttleofx/gui/browser/parallelThread.py diff --git a/buttleofx/gui/browser_v2/qml/ActionManager.qml b/buttleofx/gui/browser/qml/ActionManager.qml similarity index 100% rename from buttleofx/gui/browser_v2/qml/ActionManager.qml rename to buttleofx/gui/browser/qml/ActionManager.qml diff --git a/buttleofx/gui/browser/qml/Browser.qml b/buttleofx/gui/browser/qml/Browser.qml index 03791768..ec34d8c7 100644 --- a/buttleofx/gui/browser/qml/Browser.qml +++ b/buttleofx/gui/browser/qml/Browser.qml @@ -1,161 +1,99 @@ -import QtQuick 2.1 -import QtQuick.Layouts 1.0 -import ButtleFileModel 1.0 -import QtQuick.Controls 1.0 - +import QtQuick 2.2 +import QtQuick.Layouts 1.1 import "../../../gui" Rectangle { - id: browser + id: root + + width: 800 + height: 600 color: "#353535" + property alias fileWindow: fileWindow + property alias navBar: navBar + property bool showTab: true + property variant bModel: _browser + property variant bAction: _browserAction + property int visitedFolderListIndex: 0 + signal buttonCloseClicked(bool clicked) signal buttonFullscreenClicked(bool clicked) - QtObject { - id: m - property string directory: _browser.getFirstFolder() - property string fileFolder: "/" - property int nbInSeq - property string filter:"*" - property variant selected - property bool showSeq: false - } - - ListModel { - id: listPrevious + function pushVisitedFolder(path){ + if (visitedFolderList.count === 0){ + // Save path of the current folder + visitedFolderList.append({"url": _browser.currentPath}) + } + visitedFolderList.append({"url": path}) + ++ visitedFolderListIndex } - FileModelBrowser { - id: firstFile + function popVisitedFolder(){ + if (visitedFolderList.count > 0 && visitedFolderListIndex > 0) { + -- visitedFolderListIndex + bModel.currentPath = visitedFolderList.get(visitedFolderListIndex).url + } } - focus: true - - Tab { - id: tabBar - name: "Browser" - onCloseClicked: browser.buttonCloseClicked(true) - onFullscreenClicked: browser.buttonFullscreenClicked(true) + // Recently visited folder stack + ListModel { + id: visitedFolderList } ColumnLayout { anchors.fill: parent - anchors.topMargin: tabBar.height + spacing: 0 - HeaderBar { - id: headerBar - y: tabBar.height - z: files.z + 1 + Tab { + id: tabBar Layout.fillWidth: true - Layout.preferredHeight: 40 - - listPrevious: listPrevious - parentFolder: m.fileFolder - folder: m.directory - - onChangeFolder: { - m.directory = folder - } - onRefreshFolder: { - files.forceActiveFocusOnRefresh() - } - onChangeSeq: { - m.showSeq = seq - } + name: "Browser" + visible: root.showTab + onCloseClicked: root.buttonCloseClicked(true) + onFullscreenClicked: root.buttonFullscreenClicked(true) } - WindowFiles { - id: files + NavBar { + id: navBar Layout.fillWidth: true - Layout.fillHeight: true - Layout.preferredHeight: 120 - z: 1 - - showSeq: m.showSeq - viewList: headerBar.isInListView - folder: m.directory - filterName: m.filter - - onGoToFolder: { - listPrevious.append({"url": m.directory}) - m.directory= newFolder - } - onChangeFileFolder: { - m.fileFolder = fileFolder - } - onChangeNbFilesInSeq: { - m.nbInSeq = nb - } - onChangeSelectedList: { - m.selected = selected + Layout.preferredHeight: childrenRect.height + + property var model: root.bModel + property alias visitedFolderList: visitedFolderList + property alias visitedFolderListIndex: root.visitedFolderListIndex + + onPushVisitedFolder: { + root.pushVisitedFolder(path) } } - FooterBar { - id: footerBar + Rectangle { + id: separator Layout.fillWidth: true - Layout.preferredHeight: 40 - - selected: m.selected - nbInSeq: m.nbInSeq - - onChangeFilter: { - m.filter = newFilter - } - onOpenFolder: { - listPrevious.append({"url": m.directory}) - m.directory = newFolder - } + Layout.preferredHeight: 1 + color: "#00b2a1" } - } + // Main window with files list + FileWindow{ + id: fileWindow + Layout.fillWidth: true + Layout.fillHeight: true + + property var model: root.bModel + property var bAction: root.bAction + property alias visitedFolderList: visitedFolderList + property alias visitedFolderListIndex: root.visitedFolderListIndex + + onPushVisitedFolder: { + root.pushVisitedFolder(path) + } + } + } - Keys.onPressed: { - if (event.key == Qt.Key_Tab) { - headerBar.forceActiveFocusOnPathWithTab() - event.accepted = true - } - if (event.key == Qt.Key_Return) { - files.enterFolder() - event.accepted = true - } - if ((event.key == Qt.Key_L) && (event.modifiers & Qt.ControlModifier)) { - headerBar.forceActiveFocusOnPath() - event.accepted = true - } - if ((event.key == Qt.Key_N) && (event.modifiers & Qt.ControlModifier)) { - files.forceActiveFocusOnCreate() - event.accepted = true - } - if (event.key == Qt.Key_F2) { - files.forceActiveFocusOnRename() - event.accepted = true - } - if (event.key == Qt.Key_F5) { - files.forceActiveFocusOnRefresh() - event.accepted = true - } - if (event.key == Qt.Key_Delete) { - files.forceActiveFocusOnDelete() - event.accepted = true - } - if (event.key == Qt.Key_Right) { - files.forceActiveFocusOnChangeIndexOnRight() - event.accepted = true - } - if (event.key == Qt.Key_Left) { - files.forceActiveFocusOnChangeIndexOnLeft() - event.accepted = true - } - if (event.key == Qt.Key_Down) { - files.forceActiveFocusOnChangeIndexOnDown() - event.accepted = true - } - if (event.key == Qt.Key_Up) { - files.forceActiveFocusOnChangeIndexOnUp() - event.accepted = true + Keys.onReleased: { + if ((event.modifiers & Qt.ControlModifier) && (event.key == Qt.Key_L)){ + navBar.toggleUrlEdit() } } - } diff --git a/buttleofx/gui/browser_v2/qml/BrowserStandalone.qml b/buttleofx/gui/browser/qml/BrowserStandalone.qml similarity index 100% rename from buttleofx/gui/browser_v2/qml/BrowserStandalone.qml rename to buttleofx/gui/browser/qml/BrowserStandalone.qml diff --git a/buttleofx/gui/browser/qml/Button.qml b/buttleofx/gui/browser/qml/Button.qml deleted file mode 100644 index 6b39dac5..00000000 --- a/buttleofx/gui/browser/qml/Button.qml +++ /dev/null @@ -1,45 +0,0 @@ -import QtQuick 2.1 - -Rectangle { - id: button - - width: 150 - height: 35 - radius: 2 - - property color buttonColor: "lightblue" - property color onHoverColor: "blue" - property color borderColor: "white" - - property string text: "Button" - - signal buttonClick() - - onButtonClick: { - console.log(buttonLabel.text + " clicked") - } - - border { - color: button.borderColor - } - - Text { - id: buttonLabel - anchors.centerIn: parent - text: button.text - } - - MouseArea { - id: buttonMouseArea - - // Anchor all sides of the mouse area to the rectangle's anchors - anchors.fill: parent - - onClicked: buttonClick() - hoverEnabled: true - onEntered: parent.color = onHoverColor - onExited: parent.color = buttonColor - } - - color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5): buttonColor -} diff --git a/buttleofx/gui/browser/qml/FileInfo.qml b/buttleofx/gui/browser/qml/FileInfo.qml deleted file mode 100644 index f5418f19..00000000 --- a/buttleofx/gui/browser/qml/FileInfo.qml +++ /dev/null @@ -1,236 +0,0 @@ -import QtQuick 2.0 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Controls.Styles 1.0 - -ApplicationWindow { - id: fileInfo - - property variant currentFile - signal refreshFolder() - signal deleteItem() - - property color background: "#141414" - property color backgroundInput: "#343434" - property color gradian1: "#010101" - property color gradian2: "#141414" - property color borderInput: "#444" - - property color textColor: "white" - property color activeFocusOn: "white" - property color activeFocusOff: "grey" - - minimumWidth: 280 - minimumHeight: 50 - maximumWidth: minimumWidth - maximumHeight: 500 - flags: Qt.FramelessWindowHint | Qt.SplashScreen - - height: fileLoader.childrenRect.height - color: fileInfo.background - - onVisibleChanged: { - if (visible) - rootMouseArea.forceActiveFocus() - } - - MouseArea { - id: rootMouseArea - anchors.fill: parent - hoverEnabled: true - onContainsMouseChanged: { - // Hack TODO: need another solution to hide the window - if (!containsMouse) - { - fileInfo.visible = ! (mouseX <= 3 || mouseY <= 3 || - mouseX >= rootMouseArea.width - 3 || mouseY >= rootMouseArea.height - 3) - } - } - } - - Item { - id: marginItem - anchors.fill: parent - anchors.margins: 5 - - Loader { - id: fileLoader - width: parent.width - height: childrenRect.height - sourceComponent: currentFile ? fileComponent : undefined - - Component { - id: fileComponent - - ColumnLayout { - spacing: 5 - width: parent.width - height: childrenRect.height - - // Name of the file - Rectangle { - id: fileName - width: parent.width - implicitHeight: childrenRect.height - - RowLayout { - id: fileNameContainer - spacing: 5 - width: parent.width - height: childrenRect.height - - // Title - Text { - id: fileNameText - color: textColor - text: "Name: " - } - - // Input field limited to 50 characters - Rectangle { - height: fileNameText.height - Layout.fillWidth: true - implicitWidth: 200 - color: fileInfo.backgroundInput - border.width: 1 - border.color: fileInfo.borderInput - radius: 3 - clip: true - - TextInput { - id: fileNameInput - text: currentFile.fileName - anchors.fill: parent - anchors.leftMargin: 5 - anchors.rightMargin: 5 - maximumLength: 100 - selectByMouse: true - color: activeFocus ? activeFocusOn : activeFocusOff - - onAccepted: { - currentFile.fileName = fileNameInput.text - } - onActiveFocusChanged: { - currentFile.fileName = fileNameInput.text - } - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: currentFile.nameUser = currentFile.getDefaultNameUser() - } - } - } - } - - Rectangle { - id: fileDetailedInfoContainer - width: parent.width - implicitHeight: fileSizeInfoLoader.childrenRect.height + imageDimensionsLoader.childrenRect.height - - ColumnLayout { - width: parent.width - implicitHeight: childrenRect.height - - Loader { - id: fileSizeInfoLoader - sourceComponent: currentFile.fileType != 'Folder' ? fileSizeInfo : undefined - } - - Loader { - id: imageDimensionsLoader - sourceComponent: (currentFile.fileType != 'Folder' && currentFile.getSupported() ? - imageDimensionsInfo : undefined) - } - - Component { - id: fileSizeInfo - - // Size of file - RowLayout { - id: fileSize - width: parent.width - - Text { - id: fileSizeText - color: textColor - text: "Size: " - } - - Text { - id: fileSizeInput - text: (currentFile.fileWeight > 1000000 ? (currentFile.fileWeight / 1000000).toFixed(2) - + " MB" : (currentFile.fileWeight / 1000).toFixed(2) + " KB") - color: "grey" - } - } - } - - Component { - id: imageDimensionsInfo - - // Dimensions of image (only displayed if the file is an image, of course) - RowLayout { - id: imageDimensions - width: parent.width - - Text { - id: imageDimensionsText - color: textColor - text: "Dimensions: " - } - - Text { - id: imageDimensionsInput - property size imageDimensions: currentFile.imageSize - text: "width: " + imageDimensions.width + ", height: " + imageDimensions.height - color: "grey" - } - } - } - } - } - - Item { - id: remove - width: parent.width - implicitHeight: 30 - Layout.minimumHeight: 20 - - Button { - id: removeButton - height: parent.height - 10 - width: 200 - text: "Remove" - - onClicked: { - editFile=false - deleteItem() - } - } - } - } - } - } - } - - /* - MessageDialog { - id: deleteMessage - title: "Delete?" - icon: StandardIcon.Warning - text: "Do you really want to delete " + currentFile.fileName + "?" - standardButtons: StandardButton.No | StandardButton.Yes - onYes: { - deleteItem() - console.log("deleted") - editFile=false - } - onNo: { - console.log("didn't delete") - } - } - */ -} diff --git a/buttleofx/gui/browser_v2/qml/FileWindow.qml b/buttleofx/gui/browser/qml/FileWindow.qml similarity index 80% rename from buttleofx/gui/browser_v2/qml/FileWindow.qml rename to buttleofx/gui/browser/qml/FileWindow.qml index bb1c5008..ee1ebb72 100644 --- a/buttleofx/gui/browser_v2/qml/FileWindow.qml +++ b/buttleofx/gui/browser/qml/FileWindow.qml @@ -7,7 +7,8 @@ import QtQuick.Dialogs 1.1 Rectangle { id: root - color: "transparent" + color: 'transparent' + focus: true // defaults slots function onItemClickedSlot(pathImg){ @@ -51,6 +52,67 @@ Rectangle { signal pushVisitedFolder(string path) Keys.onEscapePressed: root.model.unselectAllItems() + + + Keys.onPressed: { + if(event.key === Qt.Key_F5){ + root.model.load('') + } + } + + // key navigation between browser items + Keys.onReleased: { + var k = event.key + if(k === Qt.Key_Backspace && (event.modifiers & Qt.ShiftModifier)){ + popVisitedFolder() + } + else if(k === Qt.Key_Backspace){ + if(model.currentPath !== "/" && model.currentPath.trim() !== ""){ + pushVisitedFolder(model.parentFolder) + root.model.currentPath = root.model.parentFolder + } + } + + var selectedItem = null + + // enter inside folder on enter: change model current path + // use clicked signals: wanted behavior + if(k === Qt.Key_Enter || k === Qt.Key_Return){ + var indexSelected = grid.currentIndex + selectedItem = root.model.fileItems.get(indexSelected) + + if(selectedItem.isFolder()){ + root.model.currentPath = selectedItem.path + return + } + + root.model.selectItem(indexSelected) + root.itemDoubleClicked(selectedItem.path, selectedItem.path, selectedItem.isFolder(), selectedItem.isSupported()) + root.itemClicked(selectedItem.path, selectedItem.path, selectedItem.isFolder(), selectedItem.isSupported()) + + if(!selectedItem.isSupported()) + selectedItem.launchDefaultApplication() + } + + if(k === Qt.Key_Up) + grid.moveCurrentIndexUp() + else if (k === Qt.Key_Down) + grid.moveCurrentIndexDown() + else if (k === Qt.Key_Left) + grid.moveCurrentIndexLeft() + else if (k === Qt.Key_Right) + grid.moveCurrentIndexRight() + else + return + + if(event.modifiers & Qt.ShiftModifier) + root.model.selectItemTo(grid.currentIndex) + else + root.model.selectItem(grid.currentIndex) + + + } + MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton @@ -66,15 +128,41 @@ Rectangle { } } - Keys.onPressed: { - //TODO: temporary shortcut menu: overload ambiguous + MessageDialog { + id: defaultApplicationFail + title: "Error while opening file with system" + text: "" } Menu{ - //TODO: REDO architecture id:actionsMenu property bool showActionOnItem: false + MenuItem{ + id: defaultApplication + text:"Open system application" + iconName: "document-open" + shortcut: StandardKey.Open + visible:actionsMenu.showActionOnItem + + onTriggered: { + var itemsSelected = root.model.selectedItems + var failedFiles = [] + + for(var i=0; i 1 ? 's' : '') + '.' + defaultApplicationFail.visible = true + } + + } + } + + MenuItem{ id: select text:"Select all" @@ -147,7 +235,7 @@ Rectangle { shortcut: StandardKey.Paste enabled: root.bAction.isCache onTriggered: { - var destination="" + var destination = "" if(root.model.selectedItems.count == 1 && root.model.selectedItems.get(0).isFolder()) destination = root.model.selectedItems.get(0).path @@ -213,8 +301,17 @@ Rectangle { model: root.model.fileItems delegate: component + keyNavigationWraps: true boundsBehavior: Flickable.StopAtBounds - focus: true + + // set the properly current selected when model ends loading + Connections{ + target: root.model + onLoadingChanged: { + if(!root.model.loading) + grid.currentIndex = -1 + } + } } } @@ -227,7 +324,7 @@ Rectangle { width: grid.cellWidth - 20 height: icon.height + fileName.height - color: (model.object.isSelected) ? "#666666" : "transparent" + color: model.object.isSelected ? "#666666" : "transparent" radius: 2 Column { @@ -329,6 +426,8 @@ Rectangle { else if(mouse.button == Qt.LeftButton){ root.itemClicked(model.object.path, model.object.path, model.object.isFolder(), model.object.isSupported()) + grid.currentIndex = index + if ((mouse.modifiers & Qt.ShiftModifier)) root.model.selectItemTo(index) else if ((mouse.modifiers & Qt.ControlModifier)) diff --git a/buttleofx/gui/browser/qml/FooterBar.qml b/buttleofx/gui/browser/qml/FooterBar.qml deleted file mode 100644 index 1d771fd1..00000000 --- a/buttleofx/gui/browser/qml/FooterBar.qml +++ /dev/null @@ -1,77 +0,0 @@ -import QtQuick 2.1 -import QtQuick.Layouts 1.0 -import QtQuick.Controls 1.0 - -Rectangle { - id: footer - color: "#141414" - - property string filter: "*" - property variant selected - property int nbInSeq - - signal changeFilter(string newFilter) - signal openFolder(string newFolder) - - RowLayout { - anchors.fill: parent - spacing: 6 - anchors.leftMargin: spacing - anchors.rightMargin: spacing - - Item { - Layout.fillHeight: true - Layout.fillWidth: true - } - - Text { - id: nbOfFiles - - text: { - if (selected.count > 1) { - selected.count + " files selected" - } else if (selected.count == 1) { - selected.get(0).fileType == "Sequence" ? nbInSeq + " files in Sequence" : "" - } else { - "" - } - } - - color: "white" - } - - ComboBox { - width: 200 - model: ["*", "Tuttle Readable"] - - onCurrentTextChanged: { - changeFilter(currentText) - // console.log("currentText = " + currentText) - } - } - - Button { - id: openButton - text: "Import" - height: parent.height - - // Import selected files in the graph - onClicked: { - _buttleData.currentGraphWrapper = _buttleData.graphWrapper - _buttleData.currentGraphIsGraph() - - // If before the viewer was showing an image from the brower, we change the currentView - if (_buttleData.currentViewerIndex > 9) { - _buttleData.currentViewerIndex = player.lastView - if (player.lastNodeWrapper != undefined) - _buttleData.currentViewerNodeWrapper = player.lastNodeWrapper - player.changeViewer(player.lastView) - } - - for (var i = 0; i < selected.count; ++i) { - _buttleManager.nodeManager.dropFile(selected.get(i).filepath, 10 * i, 10 * i) - } - } - } - } -} diff --git a/buttleofx/gui/browser/qml/HeaderBar.qml b/buttleofx/gui/browser/qml/HeaderBar.qml deleted file mode 100644 index 1ec0307d..00000000 --- a/buttleofx/gui/browser/qml/HeaderBar.qml +++ /dev/null @@ -1,328 +0,0 @@ -import QtQuick 2.1 -import ButtleFileModel 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Controls 1.0 -import QtQuick.Controls.Styles 1.0 - - -Rectangle { - id: headerBar - color: "#141414" - - property string folder - signal changeFolder(string folder) - signal refreshFolder() - property string parentFolder - property variant listPrevious - property bool isInListView: false - signal changeSeq(bool seq) - property bool withTab: false - - function forceActiveFocusOnPath() { - withTab = false - texteditPath.forceActiveFocus() - } - - function forceActiveFocusOnPathWithTab() { - withTab = true - texteditPath.forceActiveFocus() - } - - FileModelBrowser { - id: suggestion - folder: headerBar.folder - } - - ListModel { - id: nextList - } - - RowLayout { - spacing: 6 - anchors.fill: parent - anchors.leftMargin: spacing - anchors.rightMargin: spacing - - Button { - id: previous - width: 15 - height: 15 - - iconSource: - if (hovered) { - "../../img/buttons/browser/previous_hover.png" - } else { - "../../img/buttons/browser/previous.png" - } - - style: - ButtonStyle { - background: Rectangle { - anchors.fill: parent - color: "transparent" - } - } - - onClicked: { - if (listPrevious.count > 0) { - nextList.append({"url": headerBar.folder}) - changeFolder(listPrevious.get(listPrevious.count - 1).url) - listPrevious.remove(listPrevious.count - 1) - } - } - } - - Button { - id: next - width: 15 - height: 15 - - iconSource: - if (hovered) { - "../../img/buttons/browser/next_hover.png" - } else { - "../../img/buttons/browser/next.png" - } - - style: - ButtonStyle { - background: Rectangle { - anchors.fill: parent - color: "transparent" - } - } - - onClicked: { - if (nextList.count > 0) { - listPrevious.append({"url": headerBar.folder}) - changeFolder(nextList.get(nextList.count - 1).url) - nextList.remove(nextList.count - 1) - } - } - } - - Button { - id: parent_folder - width: 15 - height: 15 - - iconSource: - if (hovered) { - "../../img/buttons/browser/parent_hover.png" - } else { - "../../img/buttons/browser/parent.png" - } - - style: - ButtonStyle { - background: Rectangle { - anchors.fill: parent - color: "transparent" - } - } - - onClicked: changeFolder(parentFolder) - } - - Rectangle { - id: textEditContainer - height: 24 - Layout.fillWidth: true - - color: "black" - border.color: "grey" - radius: 5 - - TextInput { - id: texteditPath - y: 4 - x: 5 - height: parent.height - width: parent.width - 10 - clip: true - - text: headerBar.folder - - color: suggestion.exists ? "white" : "red" - selectByMouse: true - selectionColor: "#00b2a1" - - onAccepted: { - if (acceptableInput) { - listPrevious.append({"url": headerBar.folder}) - changeFolder(text) - textEditContainer.forceActiveFocus() - } - } - - onFocusChanged: { - if (texteditPath.focus) { - if (!withTab) { - selectAll() - } - } else { - if (acceptableInput) { - listPrevious.append({"url": headerBar.folder}) - changeFolder(text) - } - } - } - onTextChanged: { - suggestion.folder = texteditPath.getText(0, texteditPath.cursorPosition + 1) - } - onCursorPositionChanged: { - suggestion.folder = texteditPath.getText(0, texteditPath.cursorPosition + 1) - } - - validator: RegExpValidator { - regExp: - if (!suggestion.isEmpty()) { - /suggestion.getFilteredFileItems(suggestion.folder).get(0).filepath/ - } else { - /.*/ - } - } - - Keys.onTabPressed: { - suggestionsMenu.show() - texteditPath.forceActiveFocus() - } - } - - Menu { - id: suggestionsMenu - // __minimumWidth: textEditContainer.width - // __xOffset: -13 // don't know how to remove icon space on menuItems - // __yOffset: 0 - __visualItem: textEditContainer - // style: __style.__popupStyle //__style.__dropDownStyle - - // property ExclusiveGroup eg: ExclusiveGroup { id: eg } - - Instantiator { - model: suggestion.getFilteredFileItems(suggestion.folder) - - MenuItem { - id: textComponent - text: model.object.fileName - - onTriggered: { - changeFolder(model.object.filepath) - } - // checkable: true - // exclusiveGroup: eg - } - - onObjectAdded: suggestionsMenu.insertItem(index, object) - onObjectRemoved: suggestionsMenu.removeItem(object) - } - - function show() { - // Retrieve position of last "/" instead of cursorRectangle.x - var index = suggestion.folder.lastIndexOf("/") - var x = 0 - if (index != -1) { - var rect = texteditPath.positionToRectangle(index) - x = rect.x - } - - var y = texteditPath.height - suggestionsMenu.__popup(x, y) - } - } - } - - Button { - id: refresh - width: 1 - height: 1 - - iconSource: - if (hovered) { - "../../img/buttons/browser/refresh_hover.png" - } else { - "../../img/buttons/browser/refresh.png" - } - - style: - ButtonStyle { - background: Rectangle { - anchors.fill: parent - color: "transparent" - } - } - - onClicked: refreshFolder() - } - - Button { - id: view - width: 12 - height: 12 - - iconSource: - if (hovered) { - "../../img/buttons/browser/listview_hover.png" - } else { - "../../img/buttons/browser/listview.png" - } - - states: [ - State { - name: "gridview" - when: headerBar.isInListView == true - PropertyChanges { - target: view - iconSource: - if (hovered) { - "../../img/buttons/browser/gridview_hover.png" - } else { - "../../img/buttons/browser/gridview.png" - } - } - }, - State { - name: "listview" - when: headerBar.isInListView == false - PropertyChanges { - target: view - iconSource: - if (hovered) { - "../../img/buttons/browser/listview_hover.png" - } else { - "../../img/buttons/browser/listview.png" - } - } - } - ] - - style: - ButtonStyle { - background: Rectangle { - anchors.fill: parent - color: "transparent" - } - } - - onClicked: - if (isInListView) { - isInListView = false - } else { - isInListView = true - } - } - - CheckBox { - id: check - - style: CheckBoxStyle { - label: Text { - text: "Seq" - color: "white" - } - } - - onClicked: headerBar.changeSeq(check.checked) - } - } -} diff --git a/buttleofx/gui/browser/qml/LeftCol.qml b/buttleofx/gui/browser/qml/LeftCol.qml index 252fb9e0..4f81f5d9 100644 --- a/buttleofx/gui/browser/qml/LeftCol.qml +++ b/buttleofx/gui/browser/qml/LeftCol.qml @@ -1,46 +1,21 @@ -import QtQuick 2.1 +import QtQuick 2.0 import Qt.labs.folderlistmodel 1.0 -Rectangle { - id: leftColumn - color: "red" +ListView { + width: 200 + height: 400 - ListView { - id: listview - y: 20 - x: 10 - height: parent.height - width: parent.height - currentIndex: -1 + FolderListModel { + id: folderModel + folder:"/home/jordi" + nameFilters: ["*"] + } - model: FolderListModel { - id: folders - folder: "/home/lucie-linux/" - showDirsFirst: true - } + Component { + id: fileDelegate + Text { text: fileName } + } - delegate: Column { - Text { - text: fileName - anchors.horizontalCenter: parent.horizontalCenter - - MouseArea { - id: mouseRegion - anchors.fill: parent - - onClicked: { - console.log( fileName + " Clicked") - listview.currentIndex = index - } - } - } - } - - highlight: Rectangle { - color: "lightsteelblue" - radius: 5 - } - - keyNavigationWraps: false - } -} + model: folderModel + delegate: fileDelegate + } diff --git a/buttleofx/gui/browser_v2/qml/NavBar.qml b/buttleofx/gui/browser/qml/NavBar.qml similarity index 99% rename from buttleofx/gui/browser_v2/qml/NavBar.qml rename to buttleofx/gui/browser/qml/NavBar.qml index 44fb866b..6e8cc16a 100644 --- a/buttleofx/gui/browser_v2/qml/NavBar.qml +++ b/buttleofx/gui/browser/qml/NavBar.qml @@ -21,7 +21,6 @@ Rectangle { texteditPath.forceActiveFocus() } - Component.onCompleted: toggleUrlEdit(true) QtObject { id: m; @@ -520,7 +519,7 @@ Rectangle { } } onAccepted: { - root.model.loadData(text.trim()) + root.model.load(text.trim()) } onFocusChanged: { diff --git a/buttleofx/gui/browser/qml/Viewer.qml b/buttleofx/gui/browser/qml/Viewer.qml deleted file mode 100644 index 71f9ec7e..00000000 --- a/buttleofx/gui/browser/qml/Viewer.qml +++ /dev/null @@ -1,14 +0,0 @@ -import QtQuick 2.0 - -Rectangle { - id: viewer - color: "red" - property string filepath - - Image { - x: 25 - source: viewer.filepath - sourceSize.width: parent.width - 20 - sourceSize.height: parent.height - 20 - } -} diff --git a/buttleofx/gui/browser/qml/WindowFiles.qml b/buttleofx/gui/browser/qml/WindowFiles.qml deleted file mode 100644 index 53412343..00000000 --- a/buttleofx/gui/browser/qml/WindowFiles.qml +++ /dev/null @@ -1,695 +0,0 @@ -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import ButtleFileModel 1.0 -import QtQuick.Dialogs 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Controls.Styles 1.0 - - -Rectangle { - id: winFile - color: fileModel.exists ? "black" : "lightgrey" - - property string folder: fileModel.firstFolder() - signal goToFolder(string newFolder) - property string filterName - signal changeFileFolder(string fileFolder) - signal changeNbFilesInSeq(int nb) - property bool viewList: false - signal changeSelectedList(variant selected) - property int itemIndex: 0 - property bool showSeq: false - property int nbCell: viewList ? 1 : gridview.width/gridview.cellWidth - - function showEditFile(pos) { - fileInfo.visible = true - fileInfo.x = mainWindowQML.x + pos.x - 5 - fileInfo.y = mainWindowQML.y + pos.y - 5 - } - - function enterFolder() { - var lastSelected = fileModel.getLastSelected() - if (lastSelected.getFileType() == 'Folder') { - winFile.goToFolder(lastSelected.getFilepath()) - } - } - - function forceActiveFocusOnCreate() { - fileModel.createFolder(fileModel.folder + "/New Directory") - } - - function forceActiveFocusOnDelete() { - fileModel.deleteItem(itemIndex) - winFile.forceActiveFocusOnRefresh() - } - - function selectItem(index) { - fileModel.selectItem(index) - var sel = fileModel.getSelectedItems() - - // If it's an image, we assign it to the viewer - if (sel && !sel.isEmpty()) { - if (sel.get(0).fileType != "Folder" && sel.get(0).getSupported()) { - player.changeViewer(11) // We come to the temporary viewer - // We save the last node wrapper of the last view - player.lastNodeWrapper = _buttleData.getNodeWrapperByViewerIndex(player.lastView) - - readerNode.nodeWrapper = _buttleData.nodeReaderWrapperForBrowser(sel.get(0).filepath) - - _buttleData.currentGraphIsGraphBrowser() - _buttleData.currentGraphWrapper = _buttleData.graphBrowserWrapper - - _buttleData.currentViewerNodeWrapper = readerNode.nodeWrapper - _buttleData.currentViewerFrame = 0 - // We assign the node to the viewer, at the frame 0 - _buttleData.assignNodeToViewerIndex(readerNode.nodeWrapper, 10) - _buttleData.currentViewerIndex = 10 // We assign to the viewer the 10th view - _buttleEvent.emitViewerChangedSignal() - } - } - } - - function forceActiveFocusOnRefresh() { - fileModel.updateFileItems(fileModel.folder) - winFile.selectItem(itemIndex) - } - - function forceActiveFocusOnChangeIndexOnRight() { - if (itemIndex < fileModel.size) { - winFile.selectItem(++itemIndex) - } - } - - function forceActiveFocusOnChangeIndexOnLeft() { - if (itemIndex > 0) { - winFile.selectItem(--itemIndex) - } - } - - function forceActiveFocusOnChangeIndexOnDown() { - if (itemIndex + winFile.nbCell < fileModel.size) { - itemIndex += winFile.nbCell - winFile.selectItem(itemIndex) - } else { - itemIndex = fileModel.size - winFile.selectItem(itemIndex) - } - } - - function forceActiveFocusOnChangeIndexOnUp() { - if (itemIndex - winFile.nbCell >= 0) { - itemIndex -= winFile.nbCell - winFile.selectItem(itemIndex) - } else { - itemIndex = 0 - winFile.selectItem(itemIndex) - } - } - - MouseArea { - anchors.fill: parent - - onClicked: { - forceActiveFocus() - // TODO: unselect - } - } - - Menu { - id: creation - - MenuItem { - text: "Create a Directory" - - onTriggered: { - fileModel.createFolder(fileModel.folder + "/New Directory") - } - } - } - - - QtObject { - id: readerNode - property variant nodeWrapper - } - - FileModelBrowser { - id: fileModel - folder: winFile.folder - nameFilter: winFile.filterName - showSeq: winFile.showSeq - - onFolderChanged: { - winFile.changeFileFolder(fileModel.parentFolder) - winFile.changeSelectedList(fileModel.getSelectedItems()) - winFile.selectItem(0) - itemIndex = 0 - } - onNameFilterChanged: { - winFile.changeFileFolder(fileModel.parentFolder) - winFile.changeSelectedList(fileModel.getSelectedItems()) - winFile.selectItem(0) - itemIndex = 0 - } - onShowSeqChanged: { - winFile.changeFileFolder(fileModel.parentFolder) - winFile.changeSelectedList(fileModel.getSelectedItems()) - winFile.selectItem(0) - itemIndex = 0 - } - } - - FileInfo { - id: fileInfo - visible: false - - onRefreshFolder: { - winFile.forceActiveFocusOnRefresh() - } - onDeleteItem: { - winFile.forceActiveFocusOnDelete() - } - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - - onClicked: { - if (mouse.button == Qt.RightButton) - creation.popup() - } - } - - Text { - id: hack_fontMetrics - text: "A" - visible: false - } - - // Code for the (default) grid layout - ScrollView { - anchors.fill: parent - anchors.topMargin: 5 - anchors.bottomMargin: 5 - height: 120 - width: 110 - visible: viewList ? false : true - - style: ScrollViewStyle { - scrollBarBackground: Rectangle { - id: scrollBar - width: 15 - color: "#212121" - border.width: 1 - border.color: "#333" - } - - decrementControl : Rectangle { - id: scrollLower - width: 15 - height: 15 - color: styleData.pressed ? "#212121" : "#343434" - border.width: 1 - border.color: "#333" - radius: 3 - - Image { - id: arrow - source: "file:///" + _buttleData.buttlePath + "/gui/img/buttons/params/arrow2.png" - x: 4 - y: 4 - } - } - - incrementControl : Rectangle { - id: scrollHigher - width: 15 - height: 15 - color: styleData.pressed ? "#212121" : "#343434" - border.width: 1 - border.color: "#333" - radius: 3 - - Image { - id: arrow - source: "file:///" + _buttleData.buttlePath + "/gui/img/buttons/params/arrow.png" - x: 4 - y: 4 - } - } - } - - GridView { - id: gridview - width: parent.width - height: parent.height - cellWidth: 120 - cellHeight: cellWidth - property int gridMargin: 4 - visible: !viewList - boundsBehavior: Flickable.StopAtBounds - flickableDirection: Flickable.VerticalFlick - interactive: false - currentIndex: -1 - cacheBuffer: 10 * cellHeight // Caches 10 lines below and above - - property int previousIndex: -1 - - model: fileModel.fileItems - - delegate: Component { - id: componentInColumn - - Rectangle { - id: rootFileItem - color: model.object.isSelected ? "#00b2a1" : "transparent" - width: gridview.cellWidth - gridview.gridMargin - height: gridview.cellHeight - gridview.gridMargin - radius: 5 - objectName: index - property variant selectedFiles - - /*DropArea { - id: moveItemInColumn - anchors.fill: parent - objectName: model.object.filepath - keys: ["internFileDrag"] - - onDropped: { - console.debug("file: " + Drag.source.objectName) - console.debug("Index: " + drop.source.objectName) - //fileModel.moveItem(drop.source.objectName, ) - } - }*/ - - Drag.active: rootFileItem_mouseArea.drag.active - Drag.hotSpot.x: 20 - Drag.hotSpot.y: 20 - // Drag.dragType: Drag.Automatic - // Drag.mimeData: {"urls": [rootFileItem.selectedFiles]} - // Drag.mimeData: {"text/plain": file.filePath, "text/uri-list": ""} - // Drag.keys: "text/uri-list" - Drag.keys: "internFileDrag" - - StateGroup { - id: fileStateColumn - - states: - State { - name: "dragging" - when: rootFileItem_mouseArea.pressed - PropertyChanges { target: rootFileItem; x: rootFileItem.x; y: rootFileItem.y } - } - } - - MouseArea { - id: rootFileItem_mouseArea - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onReleased: rootFileItem.Drag.drop() - drag.target: rootFileItem - - onPressed: { - rootFileItem.forceActiveFocus() - winFile.itemIndex = index - - if (mouse.button == Qt.RightButton) - winFile.showEditFile(rootFileItem_mouseArea.mapToItem(null, mouse.x, mouse.y)) - fileModel.selectItem(index) - fileInfo.currentFile = fileModel.getSelectedItems() ? fileModel.getSelectedItems().get(0) : undefined - // options.popup() - - - // If shift: - if (mouse.modifiers & Qt.ShiftModifier) - fileModel.selectItemsByShift(gridview.previousIndex, index) - - gridview.previousIndex = index - // If ctrl: - if (mouse.modifiers & Qt.ControlModifier) - fileModel.selectItems(index) - else if (!(mouse.modifiers & Qt.ShiftModifier)) - fileModel.selectItem(index) - - var sel = fileModel.getSelectedItems() - var selection = new Array() - - for (var selIndex = 0; selIndex < sel.count; ++selIndex) { - selection[selIndex] = sel.get(selIndex).filepath - } - - rootFileItem.selectedFiles = selection - winFile.changeSelectedList(sel) - - // If it's an image, we assign it to the viewer - if (model.object.fileType != "Folder" && model.object.getSupported()) { - player.changeViewer(11) // We come to the temporary viewer - // We save the last node wrapper of the last view - player.lastNodeWrapper = _buttleData.getNodeWrapperByViewerIndex(player.lastView) - readerNode.nodeWrapper = _buttleData.nodeReaderWrapperForBrowser(model.object.filepath) - - _buttleData.currentGraphIsGraphBrowser() - _buttleData.currentGraphWrapper = _buttleData.graphBrowserWrapper - - _buttleData.currentViewerNodeWrapper = readerNode.nodeWrapper - _buttleData.currentViewerFrame = 0 - // We assign the node to the viewer, at the frame 0 - _buttleData.assignNodeToViewerIndex(readerNode.nodeWrapper, 10) - _buttleData.currentViewerIndex = 10 // We assign to the viewer the 10th view - _buttleEvent.emitViewerChangedSignal() - } - } - - onDoubleClicked: { - // If it's an image, we create a node - if (model.object.fileType != "Folder" && model.object.getSupported()) { - _buttleData.currentGraphWrapper = _buttleData.graphWrapper - _buttleData.currentGraphIsGraph() - - // If before the viewer was showing an image from the browser, we change the currentView - if (_buttleData.currentViewerIndex > 9) { - _buttleData.currentViewerIndex = player.lastView - - if (player.lastNodeWrapper != undefined) - _buttleData.currentViewerNodeWrapper = player.lastNodeWrapper - player.changeViewer(player.lastView) - } - - _buttleManager.nodeManager.dropFile(model.object.filepath, 10, 10) - } else if (model.object.fileType == "Folder") { - winFile.goToFolder(model.object.filepath) - } - } - } - - ColumnLayout { - id: file - spacing: 0 - anchors.fill: parent - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - - Rectangle { - anchors.fill: parent - anchors.margins: 4 - property int minSize: Math.min(width, height) - - color: if (thumbnail.status == Image.Error) - "red" - else if (thumbnail.status == Image.Null) - "lightred" - else if (thumbnail.status == Image.Loading) - "#33FFFFFF" - else - "transparent" - - Rectangle { - // TODO replace with an image - id: loading - anchors.centerIn: parent - width: 12 - height: width - visible: !thumbnail.isFolder && thumbnail.status == Image.Loading - // source: "images/loading.png" - - NumberAnimation on rotation { - from: 0 - to: 360 - running: loading.visible == true - loops: Animation.Infinite - duration: 1000 - } - - color: "lightblue" - } - - Image { - id: thumbnail - property bool isFolder: model.object.fileType == "Folder" - source: model.object.fileImg - asynchronous: true - // cache: false - - sourceSize.width: isFolder ? parent.minSize : -1 - sourceSize.height: isFolder ? parent.minSize : -1 - anchors.fill: parent - fillMode: Image.PreserveAspectFit - smooth: true - opacity: isFolder || status == Image.Ready ? 1 : 0 - - Behavior on opacity { PropertyAnimation { duration: 300 } } - } - } - } - - Item { - Layout.fillWidth: true - implicitHeight: hack_fontMetrics.height * 3 // 3 lines of text - - Rectangle { - id: filename_background - width: filename_text.width - height: filename_text.paintedHeight - - color: "white" - radius: 2 - - visible: filename_text.activeFocus - } - - Text { - id: filename_text - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - text: model.object.fileName - color: model.object.isSelected ? "black" : "white" - font.bold: model.object.isSelected - textFormat: Text.PlainText - wrapMode: Text.Wrap - - clip: !activeFocus - z: 9999 // TODO: need another solution to be truly on top. - } - } - } - } - } - } - } - - // Code for the list layout - ScrollView { - anchors.fill: parent - anchors.topMargin: 5 - anchors.bottomMargin: 5 - height: 120 - width: 110 - visible: viewList - - style: ScrollViewStyle { - scrollBarBackground: Rectangle { - id: scrollBar2 - width: 15 - color: "#212121" - border.width: 1 - border.color: "#333" - } - decrementControl : Rectangle { - id: scrollLower2 - width: 15 - height: 15 - color: styleData.pressed ? "#212121" : "#343434" - border.width: 1 - border.color: "#333" - radius: 3 - - Image { - id: arrowBis2 - source: "file:///" + _buttleData.buttlePath + "/gui/img/buttons/params/arrow2.png" - x: 4 - y: 4 - } - } - incrementControl : Rectangle { - id: scrollHigher2 - width: 15 - height: 15 - color: styleData.pressed ? "#212121" : "#343434" - border.width: 1 - border.color: "#333" - radius: 3 - - Image { - id: arrow - source: "file:///" + _buttleData.buttlePath + "/gui/img/buttons/params/arrow.png" - x: 4 - y: 4 - } - } - } - - ListView { - id: listview - height: parent.height - width: parent.width - visible: viewList - boundsBehavior: Flickable.StopAtBounds - flickableDirection: Flickable.VerticalFlick - interactive: false - currentIndex: -1 - - property int previousIndex: -1 - - model: fileModel.fileItems - delegate: Component { - - Rectangle { - id: fileInRow - color: model.object.isSelected ? "#00b2a1" : "transparent" - radius: 5 - height: hack_fontMetrics.height * 1.5 // 1.5 lines of text - width: listview.width - - property variant selectedFiles - property variant currentFile: model.object - // property variant filePath: model.object.filepath - - /*DropArea { - id: moveItemInRow - anchors.fill: parent - keys: ["internFileDrag"] - - onDropped: { - console.debug("Drag: " + drag.source.filepath) - //fileModel.moveItem(itemIndex, drag.source.filepath) - } - }*/ - - RowLayout { - anchors.fill: parent - spacing: 10 - - Image { - source: model.object.fileImg - sourceSize.width: parent.height - sourceSize.height: parent.height - Layout.fillHeight: true - Layout.preferredWidth: parent.height - fillMode: Image.PreserveAspectFit - } - - Text { - id: textInRow - Layout.fillWidth: true - Layout.fillHeight: true - Layout.minimumWidth: 40 - - text: model.object.fileName - color: model.object.isSelected ? "black" : "white" - font.bold: model.object.isSelected - } - } - - Drag.active: dragMouseAreaRow.drag.active - Drag.hotSpot.x: 20 - Drag.hotSpot.y: 20 - // Drag.dragType: Drag.Automatic - // Drag.mimeData: {"urls": [fileInRow.selectedFiles]} - // Drag.mimeData: {"text/plain": file.filePath, "text/uri-list": ""} - // Drag.keys: "text/uri-list" - Drag.keys: "internFileDrag" - - StateGroup { - id: fileStateRow - - states: State { - name: "dragging" - when: dragMouseAreaRow.pressed - PropertyChanges { target: fileInRow; x: fileInRow.x; y: fileInRow.y } - } - } - - MouseArea { - id: dragMouseAreaRow - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onReleased: fileInRow.Drag.drop() - drag.target: fileInRow - - onPressed: { - winFile.itemIndex = index - - if (mouse.button == Qt.RightButton) - winFile.showEditFile(dragMouseAreaRow.mapToItem(null, mouse.x, mouse.y)) - fileModel.selectItem(index) - fileInfo.currentFile = fileModel.getSelectedItems() ? fileModel.getSelectedItems().get(0) : undefined - - // If shift: - if (mouse.modifiers & Qt.ShiftModifier) - fileModel.selectItemsByShift(listview.previousIndex, index) - - listview.previousIndex = index - - // If ctrl: - if (mouse.modifiers & Qt.ControlModifier) - fileModel.selectItems(index) - else if (!(mouse.modifiers & Qt.ShiftModifier)) - fileModel.selectItem(index) - - var sel = fileModel.getSelectedItems() - var selection = new Array() - for (var selIndex = 0; selIndex < sel.count; ++selIndex) { - selection[selIndex] = sel.get(selIndex).filepath - } - - fileInRow.selectedFiles = selection - winFile.changeSelectedList(sel) - - // If it's an image, we assign it to the viewer - if (model.object.fileType != "Folder" && model.object.getSupported()) { - player.changeViewer(11) // We come to the temporary viewer - // We save the last node wrapper of the last view - player.lastNodeWrapper = _buttleData.getNodeWrapperByViewerIndex(player.lastView) - readerNode.nodeWrapper = _buttleData.nodeReaderWrapperForBrowser(model.object.filepath) - - _buttleData.currentGraphIsGraphBrowser() - _buttleData.currentGraphWrapper = _buttleData.graphBrowserWrapper - - _buttleData.currentViewerNodeWrapper = readerNode.nodeWrapper - _buttleData.currentViewerFrame = 0 - // We assign the node to the viewer, at the frame 0 - _buttleData.assignNodeToViewerIndex(readerNode.nodeWrapper, 10) - _buttleData.currentViewerIndex = 10 // We assign to the viewer the 10th view - _buttleEvent.emitViewerChangedSignal() - } - } - - onDoubleClicked: { - // If it's an image, we create a node - if (model.object.fileType != "Folder" && model.object.getSupported()) { - _buttleData.currentGraphWrapper = _buttleData.graphWrapper - _buttleData.currentGraphIsGraph() - - // If before the viewer was showing an image from the browser, we change the currentView - if (_buttleData.currentViewerIndex > 9){ - _buttleData.currentViewerIndex = player.lastView - - if (player.lastNodeWrapper != undefined) - _buttleData.currentViewerNodeWrapper = player.lastNodeWrapper - player.changeViewer(player.lastView) - } - - _buttleManager.nodeManager.dropFile(model.object.filepath, 10, 10) - } else if (model.object.fileType == "Folder") { - winFile.goToFolder(model.object.filepath) - } - } - } - } - } - } - } -} diff --git a/buttleofx/gui/browser_v2/qml/img/abort.png b/buttleofx/gui/browser/qml/img/abort.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/abort.png rename to buttleofx/gui/browser/qml/img/abort.png diff --git a/buttleofx/gui/browser_v2/qml/img/arrow_down.png b/buttleofx/gui/browser/qml/img/arrow_down.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/arrow_down.png rename to buttleofx/gui/browser/qml/img/arrow_down.png diff --git a/buttleofx/gui/browser_v2/qml/img/arrow_down_hover.png b/buttleofx/gui/browser/qml/img/arrow_down_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/arrow_down_hover.png rename to buttleofx/gui/browser/qml/img/arrow_down_hover.png diff --git a/buttleofx/gui/browser_v2/qml/img/arrow_up.png b/buttleofx/gui/browser/qml/img/arrow_up.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/arrow_up.png rename to buttleofx/gui/browser/qml/img/arrow_up.png diff --git a/buttleofx/gui/browser_v2/qml/img/arrow_up_hover.png b/buttleofx/gui/browser/qml/img/arrow_up_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/arrow_up_hover.png rename to buttleofx/gui/browser/qml/img/arrow_up_hover.png diff --git a/buttleofx/gui/browser_v2/qml/img/del.png b/buttleofx/gui/browser/qml/img/del.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/del.png rename to buttleofx/gui/browser/qml/img/del.png diff --git a/buttleofx/gui/browser_v2/qml/img/file-icon.png b/buttleofx/gui/browser/qml/img/file-icon.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/file-icon.png rename to buttleofx/gui/browser/qml/img/file-icon.png diff --git a/buttleofx/gui/browser_v2/qml/img/find.png b/buttleofx/gui/browser/qml/img/find.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/find.png rename to buttleofx/gui/browser/qml/img/find.png diff --git a/buttleofx/gui/browser_v2/qml/img/find_hover.png b/buttleofx/gui/browser/qml/img/find_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/find_hover.png rename to buttleofx/gui/browser/qml/img/find_hover.png diff --git a/buttleofx/gui/browser_v2/qml/img/folder-icon.png b/buttleofx/gui/browser/qml/img/folder-icon.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/folder-icon.png rename to buttleofx/gui/browser/qml/img/folder-icon.png diff --git a/buttleofx/gui/browser_v2/qml/img/gridview.png b/buttleofx/gui/browser/qml/img/gridview.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/gridview.png rename to buttleofx/gui/browser/qml/img/gridview.png diff --git a/buttleofx/gui/browser_v2/qml/img/gridview_hover.png b/buttleofx/gui/browser/qml/img/gridview_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/gridview_hover.png rename to buttleofx/gui/browser/qml/img/gridview_hover.png diff --git a/buttleofx/gui/browser_v2/qml/img/listview.png b/buttleofx/gui/browser/qml/img/listview.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/listview.png rename to buttleofx/gui/browser/qml/img/listview.png diff --git a/buttleofx/gui/browser_v2/qml/img/listview_hover.png b/buttleofx/gui/browser/qml/img/listview_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/listview_hover.png rename to buttleofx/gui/browser/qml/img/listview_hover.png diff --git a/buttleofx/gui/browser_v2/qml/img/next.png b/buttleofx/gui/browser/qml/img/next.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/next.png rename to buttleofx/gui/browser/qml/img/next.png diff --git a/buttleofx/gui/browser_v2/qml/img/next_hover.png b/buttleofx/gui/browser/qml/img/next_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/next_hover.png rename to buttleofx/gui/browser/qml/img/next_hover.png diff --git a/buttleofx/gui/browser_v2/qml/img/parent.png b/buttleofx/gui/browser/qml/img/parent.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/parent.png rename to buttleofx/gui/browser/qml/img/parent.png diff --git a/buttleofx/gui/browser_v2/qml/img/parent_hover.png b/buttleofx/gui/browser/qml/img/parent_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/parent_hover.png rename to buttleofx/gui/browser/qml/img/parent_hover.png diff --git a/buttleofx/gui/browser_v2/qml/img/previous.png b/buttleofx/gui/browser/qml/img/previous.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/previous.png rename to buttleofx/gui/browser/qml/img/previous.png diff --git a/buttleofx/gui/browser_v2/qml/img/previous_hover.png b/buttleofx/gui/browser/qml/img/previous_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/previous_hover.png rename to buttleofx/gui/browser/qml/img/previous_hover.png diff --git a/buttleofx/gui/browser_v2/qml/img/refresh.png b/buttleofx/gui/browser/qml/img/refresh.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/refresh.png rename to buttleofx/gui/browser/qml/img/refresh.png diff --git a/buttleofx/gui/browser_v2/qml/img/refresh_hover.png b/buttleofx/gui/browser/qml/img/refresh_hover.png similarity index 100% rename from buttleofx/gui/browser_v2/qml/img/refresh_hover.png rename to buttleofx/gui/browser/qml/img/refresh_hover.png diff --git a/buttleofx/gui/browser/sequenceWrapper.py b/buttleofx/gui/browser/sequenceWrapper.py index 67d5fe8b..790daf6f 100644 --- a/buttleofx/gui/browser/sequenceWrapper.py +++ b/buttleofx/gui/browser/sequenceWrapper.py @@ -1,56 +1,29 @@ import os - from PyQt5 import QtCore +from pySequenceParser import sequenceParser class SequenceWrapper(QtCore.QObject): - """ - Class SequenceWrapper defined by : - - _sequence : a sequence of images - """ - - def __init__(self, sequence): - super(SequenceWrapper, self).__init__() - - self._sequence = sequence - # ############################################ Methods exposed to QML ############################################ # + def __init__(self, sequenceParserItem, absPath, parent=None): + QtCore.QObject.__init__(self, parent) + self._sequence = sequenceParserItem.getSequence().clone() # copy object + self._firstFilePath = os.path.join(os.path.dirname(absPath), self._sequence.getFirstFilename()) + self._weight = sequenceParser.ItemStat(sequenceParserItem).sizeOnDisk - @QtCore.pyqtSlot(result=int) def getNbFiles(self): return self._sequence.getNbFiles() - # ######################################## Methods private to this class ####################################### # - - # ## Getters ## # - - def getFirstFileName(self): - return self._sequence.getFirstFilename() - def getFirstFilePath(self): - return self._sequence.getAbsoluteFirstFilename() + return self._firstFilePath - def getWeight(self): - res = 0 - for i in range(self._sequence.getFirstTime(), self._sequence.getLastTime() + self._sequence.getStep(), - self._sequence.getStep()): - fullpath = self._sequence.getAbsoluteFilenameAt(i) - if os.path.exists(fullpath): - res = res + os.stat(fullpath).st_size - return (res) / self._sequence.getNbFiles() - - def getTime(self): - return self._sequence.getDuration() - - # ## Others ## # + def getSequenceParsed(self): + return self._sequence - def __str__(self): - return 'Test' - - # ############################################# Data exposed to QML ############################################# # + def getWeight(self): + return self._weight - firstFilePath = QtCore.pyqtProperty(str, getFirstFilePath, constant=True) - firstFileName = QtCore.pyqtProperty(str, getFirstFileName, constant=True) - nbFiles = QtCore.pyqtProperty(int, getNbFiles, constant=True) - weight = QtCore.pyqtProperty(float, getWeight, constant=True) - time = QtCore.pyqtProperty(float, getTime, constant=True) + # ################################### Data exposed to QML #################################### # + weight = QtCore.pyqtProperty(int, getWeight) + firstFilePath = QtCore.pyqtProperty(str, getFirstFilePath) + nbFiles = QtCore.pyqtProperty(int, getNbFiles) diff --git a/buttleofx/gui/browser_v2/thumbnailUtil.py b/buttleofx/gui/browser/thumbnailUtil.py similarity index 100% rename from buttleofx/gui/browser_v2/thumbnailUtil.py rename to buttleofx/gui/browser/thumbnailUtil.py diff --git a/buttleofx/gui/browser_v2/actions/concreteActions/__init__.py b/buttleofx/gui/browser_v2/actions/concreteActions/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/buttleofx/gui/browser_v2/actions/concreteActions/copy.py b/buttleofx/gui/browser_v2/actions/concreteActions/copy.py deleted file mode 100644 index 03d293b1..00000000 --- a/buttleofx/gui/browser_v2/actions/concreteActions/copy.py +++ /dev/null @@ -1,69 +0,0 @@ -import os -import shutil - -from buttleofx.gui.browser_v2.actions.actionInterface import ActionInterface - - -class Copy(ActionInterface): - - def __init__(self, browserItem): - # destination must be a directory - # if not destination.isFolder(): - # raise TypeError - ActionInterface.__init__(self, browserItem) - self._srcPath = browserItem.getParentPath() - self._destPath = "" - self._framesPaths = [] - - def setDestinationPath(self, newPath): - self._destPath = newPath.strip() - - def execute(self): - browserItem = self._browserItem - destPath = self._destPath - - # Copy file - if browserItem.isFile(): - # TODO: Check destination permission in try catch - if os.path.exists(destPath): - shutil.copy2(browserItem.getPath(), destPath) - # filename = browserItem.getName() - # browserItem.updatePath(os.path.join(destPath, filename)) - # self.processed = True or browserItem.processed = True - - # Copy Folder - if browserItem.isFolder(): - # TODO: Check destination permission in try catch - shutil.copytree(browserItem.getPath(), os.path.join(destPath, browserItem.getName())) - # browserItem.updatePath(destPath) - - if browserItem.isSequence(): - seqParsed = browserItem.getSequence().getSequenceParsed() - frames = seqParsed.getFramesIterable() - - for f in frames: - # logging.debug(f) - filename = seqParsed.getFilenameAt(f) - # logging.debug(filename) - filePath = os.path.join(browserItem.getParentPath(), filename) - # logging.debug(file_path) - if os.path.exists(destPath): - shutil.copy2(filePath, destPath) - self._framesPaths.append(os.path.join(destPath, filename)) - - # browserItem. - - def revert(self): - browserItem = self.getBrowserItem() - browserItemName = browserItem.getName() - destPath = self._destPath - - if browserItem.isFile(): - os.remove(os.path.join(destPath, browserItemName)) - - if browserItem.isFolder(): - shutil.rmtree(os.path.join(destPath, browserItemName)) - - if browserItem.isSequence(): - for f in self._framesPaths: - os.remove(f) diff --git a/buttleofx/gui/browser_v2/main.py b/buttleofx/gui/browser_v2/main.py deleted file mode 100644 index 11889e04..00000000 --- a/buttleofx/gui/browser_v2/main.py +++ /dev/null @@ -1,46 +0,0 @@ -#! /usr/bin/env python3 - -''' - # How to run browser_v2 in standalone mode ? - copy/paste those lines to the run_buttleofx.sh - - ## Add QML2 env. var. to run browser_v2 - export QML2_IMPORT_PATH=$QT_DIR/qml - - ## Run browser_v2 in standalone mode (comment original run) - $PYTHONHOME/bin/python $BUTTLE_TOP_DIR/ButtleOFX/buttleofx/gui/browser_v2/main.py - -''' - -import os -import sys -from PyQt5.QtQml import qmlRegisterType -from buttleofx.gui.browser_v2.browserModel import BrowserModel -from pyTuttle import tuttle -from buttleofx.gui.browser_v2.standaloneUtils import ImageProvider -from PyQt5 import QtWidgets, QtQml, QtCore, QtQuick -# To prevent drivers conflicts between Mesa-utils and NVIDIA drivers on Ubuntu -from OpenGL import GL - -currentFilePath = os.path.dirname(os.path.abspath(__file__)) - -if __name__ == '__main__': - - tuttle.core().preload() - - app = QtWidgets.QApplication(sys.argv) - engine = QtQml.QQmlEngine(app) - engine.quit.connect(app.quit) - engine.addImageProvider("buttleofx", ImageProvider()) - - view = QtQuick.QQuickView(engine, None) - rc = view.rootContext() - - qmlRegisterType(BrowserModel, 'BrowserModel', 1, 0, 'BrowserModel') - qmlFilePath = os.path.join(currentFilePath, "qml/Browser.qml") - - view.setSource(QtCore.QUrl(qmlFilePath)) - view.setResizeMode(QtQuick.QQuickView.SizeRootObjectToView) - - view.show() - app.exec_() diff --git a/buttleofx/gui/browser_v2/qml/Browser.qml b/buttleofx/gui/browser_v2/qml/Browser.qml deleted file mode 100644 index fe2ffbad..00000000 --- a/buttleofx/gui/browser_v2/qml/Browser.qml +++ /dev/null @@ -1,103 +0,0 @@ -import QtQuick 2.2 -import QtQuick.Layouts 1.1 -import "../../../gui" - -Rectangle { - id: root - - width: 800 - height: 600 - color: "#353535" - - property alias fileWindow: fileWindow - property alias navBar: navBar - property bool showTab: true - property variant bModel: _browser - property variant bAction: _browserAction - property int visitedFolderListIndex: 0 - - signal buttonCloseClicked(bool clicked) - signal buttonFullscreenClicked(bool clicked) - - function pushVisitedFolder(path){ - if (visitedFolderList.count === 0){ - // Save path of the current folder - visitedFolderList.append({"url": _browser.currentPath}) - } - visitedFolderList.append({"url": path}) - ++ visitedFolderListIndex - } - - function popVisitedFolder(){ - if (visitedFolderList.count > 0 && visitedFolderListIndex > 0) { - -- visitedFolderListIndex - bModel.currentPath = visitedFolderList.get(visitedFolderListIndex).url - } - } - - // Recently visited folder stack - ListModel { - id: visitedFolderList - } - - ColumnLayout { - anchors.fill: parent - spacing: 0 - - Tab { - id: tabBar - Layout.fillWidth: true - name: "Browser" - visible: root.showTab - onCloseClicked: root.buttonCloseClicked(true) - onFullscreenClicked: root.buttonFullscreenClicked(true) - } - - NavBar { - id: navBar - Layout.fillWidth: true - Layout.preferredHeight: childrenRect.height - - property var model: root.bModel - property alias visitedFolderList: visitedFolderList - property alias visitedFolderListIndex: root.visitedFolderListIndex - - onPushVisitedFolder: { - root.pushVisitedFolder(path) - } - } - - Rectangle { - id: separator - Layout.fillWidth: true - Layout.preferredHeight: 1 - color: "#00b2a1" - } - - // Main window with files list - FileWindow{ - id: fileWindow - Layout.fillWidth: true - Layout.fillHeight: true - - property var model: root.bModel - property var bAction: root.bAction - property alias visitedFolderList: visitedFolderList - property alias visitedFolderListIndex: root.visitedFolderListIndex - - onPushVisitedFolder: { - root.pushVisitedFolder(path) - } - } - } - - Keys.onPressed: { - if ((event.modifiers & Qt.ControlModifier) && (event.key == Qt.Key_L)){ - navBar.toggleUrlEdit() - } - - if (event.key == Qt.Key_Backspace){ - popVisitedFolder() - } - } -} diff --git a/buttleofx/gui/browser_v2/qml/LeftCol.qml b/buttleofx/gui/browser_v2/qml/LeftCol.qml deleted file mode 100644 index 4f81f5d9..00000000 --- a/buttleofx/gui/browser_v2/qml/LeftCol.qml +++ /dev/null @@ -1,21 +0,0 @@ -import QtQuick 2.0 -import Qt.labs.folderlistmodel 1.0 - -ListView { - width: 200 - height: 400 - - FolderListModel { - id: folderModel - folder:"/home/jordi" - nameFilters: ["*"] - } - - Component { - id: fileDelegate - Text { text: fileName } - } - - model: folderModel - delegate: fileDelegate - } diff --git a/buttleofx/gui/browser_v2/sequenceWrapper.py b/buttleofx/gui/browser_v2/sequenceWrapper.py deleted file mode 100644 index 790daf6f..00000000 --- a/buttleofx/gui/browser_v2/sequenceWrapper.py +++ /dev/null @@ -1,29 +0,0 @@ -import os -from PyQt5 import QtCore -from pySequenceParser import sequenceParser - - -class SequenceWrapper(QtCore.QObject): - - def __init__(self, sequenceParserItem, absPath, parent=None): - QtCore.QObject.__init__(self, parent) - self._sequence = sequenceParserItem.getSequence().clone() # copy object - self._firstFilePath = os.path.join(os.path.dirname(absPath), self._sequence.getFirstFilename()) - self._weight = sequenceParser.ItemStat(sequenceParserItem).sizeOnDisk - - def getNbFiles(self): - return self._sequence.getNbFiles() - - def getFirstFilePath(self): - return self._firstFilePath - - def getSequenceParsed(self): - return self._sequence - - def getWeight(self): - return self._weight - - # ################################### Data exposed to QML #################################### # - weight = QtCore.pyqtProperty(int, getWeight) - firstFilePath = QtCore.pyqtProperty(str, getFirstFilePath) - nbFiles = QtCore.pyqtProperty(int, getNbFiles) diff --git a/buttleofx/gui/dialogs/BrowserDialog.qml b/buttleofx/gui/dialogs/BrowserDialog.qml index 122d0224..3e500b2a 100644 --- a/buttleofx/gui/dialogs/BrowserDialog.qml +++ b/buttleofx/gui/dialogs/BrowserDialog.qml @@ -8,7 +8,7 @@ import QtQuick.Controls 1.0 import QtQuick.Controls.Styles 1.0 -import "../browser_v2/qml/" +import "../browser/qml/" // common part of open and save browserDialog (Abstract behavior) // the connecitons are done in 'subclasses' diff --git a/buttleofx/gui/dialogs/BrowserSaveDialog.qml b/buttleofx/gui/dialogs/BrowserSaveDialog.qml index f2492bcd..ca82dfb5 100644 --- a/buttleofx/gui/dialogs/BrowserSaveDialog.qml +++ b/buttleofx/gui/dialogs/BrowserSaveDialog.qml @@ -8,7 +8,7 @@ import QtQuick.Controls 1.0 import QtQuick.Controls.Styles 1.0 -import "../browser_v2/qml/" +import "../browser/qml/" // BrowserSave dialog: used to save a comp. // the connecitons are done MainWindow.qml (i.e the Component which use this dialog) diff --git a/buttleofx/gui/graph/qml/GraphEditor.qml b/buttleofx/gui/graph/qml/GraphEditor.qml index 6080efa3..bf61fae5 100644 --- a/buttleofx/gui/graph/qml/GraphEditor.qml +++ b/buttleofx/gui/graph/qml/GraphEditor.qml @@ -34,19 +34,11 @@ Item { graphEditor: true x: leftColumn.width y: topLeftView.height + mainWindowQML.y + 74 + Component.onCompleted: console.log(x) StateGroup { id: statesBrowser states: [ - State { - name: "view1&2" - when: selectedView == 3 - - PropertyChanges { - target: pluginBrowser - x: mainWindowQML.x + 13 - } - }, State { name: "view3" when: selectedView != 3 diff --git a/buttleofx/gui/graph/qml/Node.qml b/buttleofx/gui/graph/qml/Node.qml index 41847a39..86cad243 100644 --- a/buttleofx/gui/graph/qml/Node.qml +++ b/buttleofx/gui/graph/qml/Node.qml @@ -249,7 +249,7 @@ Rectangle { anchors.centerIn: parent anchors.fill: parent anchors.margins: 4 * graph.zoomCoeff - color: "#bbbbbb" + color: nodeText.isSelected ? "#33333b": "#bbbbbb" radius: 8 clip: !nodeText.isSelected @@ -261,7 +261,7 @@ Rectangle { color: "#212121" opacity: 0.6 radius: 2 - visible: nodeText.isSelected ? (miniatureState ? false : true) : false + visible: nodeText.isSelected ? !miniatureState : false } Text { diff --git a/buttleofx/gui/paramEditor/qml/ParametersEditor.qml b/buttleofx/gui/paramEditor/qml/ParametersEditor.qml index 64c959ec..1298100c 100644 --- a/buttleofx/gui/paramEditor/qml/ParametersEditor.qml +++ b/buttleofx/gui/paramEditor/qml/ParametersEditor.qml @@ -134,7 +134,8 @@ Item { ListView { id: listViewParam // anchors.fill: parent - model: _buttleData.graphCanBeSaved? _buttleData.getSortedNodesWrapper():_buttleData.getSortedNodesWrapper() + model: _buttleData.editedNodesWrapper + delegate: paramDelegate } } diff --git a/buttleofx/main.py b/buttleofx/main.py index 4e75ed01..f221dd19 100644 --- a/buttleofx/main.py +++ b/buttleofx/main.py @@ -38,9 +38,9 @@ from buttleofx.event import globalButtleEvent from buttleofx.manager import globalButtleManager from buttleofx.core.undo_redo.manageTools import globalCommandManager -from buttleofx.gui.browser_v2.browserModel import BrowserModel, globalBrowser, globalBrowserDialog -from buttleofx.gui.browser_v2.actions.browserAction import globalBrowserAction, globalBrowserActionDialog -from buttleofx.gui.browser_v2.actions.browserAction import globalActionManager +from buttleofx.gui.browser.browserModel import BrowserModel +from buttleofx.gui.browser.actions.browserAction import BrowserAction +from buttleofx.gui.browser.actions.browserAction import globalActionManager from PyQt5 import QtCore, QtGui, QtQml, QtQuick, QtWidgets @@ -215,7 +215,6 @@ def main(argv, app): QtQml.qmlRegisterType(BrowserModel, "BrowserModel", 1, 0, "BrowserModel") # Add new QML type QtQml.qmlRegisterType(Finder, "FolderListViewItem", 1, 0, "FolderListView") - QtQml.qmlRegisterType(GLViewportImpl, "Viewport", 1, 0, "GLViewport") # Init undo_redo contexts @@ -232,6 +231,12 @@ def main(argv, app): globalButtleData.init(engine, currentFilePath) # Manager buttleManager = globalButtleManager.init() + # Browser: need to be initialized into main app, QFileSystemWatcher needs to be init into QThread + browser = BrowserModel(watchCurrentDir=True) + browserDialog = BrowserModel(watchCurrentDir=True) # open, save + # browser actions + browserAction = BrowserAction(browser) + browserActionDialog = BrowserAction(browserDialog) parser = argparse.ArgumentParser(description=('A command line to execute ButtleOFX, an opensource compositing ' 'software. If you pass a folder as an argument, ButtleOFX will ' @@ -239,17 +244,17 @@ def main(argv, app): parser.add_argument('folder', nargs='?', help='Folder to browse') args = parser.parse_args() - globalBrowser.setCurrentPath(os.path.abspath(args.folder) if args.folder else globalBrowser.getHomePath()) + browser.setCurrentPath(os.path.abspath(args.folder) if args.folder else browser.getHomePath()) # Expose data to QML rc = engine.rootContext() rc.setContextProperty("_buttleApp", app) rc.setContextProperty("_buttleData", globalButtleData) rc.setContextProperty("_buttleManager", buttleManager) rc.setContextProperty("_buttleEvent", globalButtleEvent) - rc.setContextProperty("_browser", globalBrowser) - rc.setContextProperty("_browserDialog", globalBrowserDialog) - rc.setContextProperty("_browserAction", globalBrowserAction) - rc.setContextProperty("_browserActionDialog", globalBrowserActionDialog) + rc.setContextProperty("_browser", browser) + rc.setContextProperty("_browserDialog", browserDialog) + rc.setContextProperty("_browserAction", browserAction) + rc.setContextProperty("_browserActionDialog", browserActionDialog) rc.setContextProperty("_actionManager", globalActionManager) iconPath = os.path.join(currentFilePath, "../blackMosquito.png") @@ -291,8 +296,8 @@ def main(argv, app): aFilter = EventFilter(app, engine) app.installEventFilter(aFilter) - globalBrowser.loadData() - globalBrowserDialog.loadData() + browser.load() + browserDialog.load() with globalActionManager: topLevelItem.show()