From 373b36406a1440173b3db0940efbf91a3e21c284 Mon Sep 17 00:00:00 2001 From: Olivier Keshavjee Date: Sun, 10 Apr 2016 17:29:27 +0200 Subject: [PATCH] Can split views --- manuskript/mainWindow.py | 10 +- manuskript/ui/editors/mainEditor.py | 97 ++++++++------ manuskript/ui/editors/mainEditor_ui.py | 20 +-- manuskript/ui/editors/mainEditor_ui.ui | 30 ++--- manuskript/ui/editors/tabSplitter.py | 163 ++++++++++++++++++++++++ manuskript/ui/editors/tabSplitter_ui.py | 41 ++++++ manuskript/ui/editors/tabSplitter_ui.ui | 63 +++++++++ manuskript/ui/style.py | 25 +++- manuskript/ui/views/corkView.py | 1 + 9 files changed, 377 insertions(+), 73 deletions(-) create mode 100644 manuskript/ui/editors/tabSplitter.py create mode 100644 manuskript/ui/editors/tabSplitter_ui.py create mode 100644 manuskript/ui/editors/tabSplitter_ui.ui diff --git a/manuskript/mainWindow.py b/manuskript/mainWindow.py index b25dfa0f..8613b224 100644 --- a/manuskript/mainWindow.py +++ b/manuskript/mainWindow.py @@ -314,9 +314,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.makeConnections() # Load settings - for i in settings.openIndexes: - idx = self.mdlOutline.getIndexByID(i) - self.mainEditor.setCurrentModelIndex(idx, newTab=True) + if settings.openIndexes: + self.mainEditor.tabSplitter.restoreOpenIndexes(settings.openIndexes) self.generateViewMenu() self.mainEditor.sldCorkSizeFactor.setValue(settings.corkSizeFactor) self.actSpellcheck.setChecked(settings.spellcheck) @@ -447,10 +446,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): if self.currentProject: # Remembering the current items (stores outlineItem's ID) - sel = [] - for i in range(self.mainEditor.tab.count()): - sel.append(self.mdlOutline.ID(self.mainEditor.tab.widget(i).currentIndex)) - settings.openIndexes = sel + settings.openIndexes = self.mainEditor.tabSplitter.openIndexes() # Save data from models if self.currentProject and settings.saveOnQuit: diff --git a/manuskript/ui/editors/mainEditor.py b/manuskript/ui/editors/mainEditor.py index 637a5ad4..837a5e5e 100644 --- a/manuskript/ui/editors/mainEditor.py +++ b/manuskript/ui/editors/mainEditor.py @@ -26,21 +26,14 @@ class mainEditor(QWidget, Ui_mainEditor): self._updating = False self.mw = mainWindow() - self.tab.tabCloseRequested.connect(self.closeTab) - self.tab.currentChanged.connect(self.tabChanged) - - # UI - try: - self.tab.setTabBarAutoHide(True) - except AttributeError: - print("Info: install Qt 5.4 or higher to use tabbar auto-hide in editor.") # Connections -------------------------------------------------------- self.sldCorkSizeFactor.valueChanged.connect( self.setCorkSizeFactor, AUC) self.btnRedacFolderCork.toggled.connect( - self.sldCorkSizeFactor.setVisible, AUC) + self.sldCorkSizeFactor.setVisible, AUC + ) self.btnRedacFolderText.clicked.connect( lambda v: self.setFolderView("text"), AUC) self.btnRedacFolderCork.clicked.connect( @@ -51,28 +44,33 @@ class mainEditor(QWidget, Ui_mainEditor): self.btnRedacFullscreen.clicked.connect( self.showFullScreen, AUC) - self.tab.setDocumentMode(False) - self.tab.setStyleSheet(style.mainEditorTabSS()) + # self.tab.setDocumentMode(False) + ############################################################################### # TABS ############################################################################### - def currentEditor(self): - return self.tab.currentWidget() + def currentTabWidget(self): + """Returns the tabSplitter that has focus.""" + ts = self.tabSplitter + while ts: + if ts.focusTab == 1: + return ts.tab + else: + ts = ts.secondTab - def tabChanged(self, index): + def currentEditor(self): + return self.currentTabWidget().currentWidget() + # return self.tab.currentWidget() + + def tabChanged(self, index=QModelIndex()): if self.currentEditor(): index = self.currentEditor().currentIndex view = self.currentEditor().folderView self.updateFolderViewButtons(view) - if index.isValid(): - hidden = not index.internalPointer().isFolder() - else: - hidden = False else: index = QModelIndex() - hidden = False self._updating = True self.mw.treeRedacOutline.setCurrentIndex(index) @@ -81,18 +79,35 @@ class mainEditor(QWidget, Ui_mainEditor): self.updateStats() self.updateThingsVisible(index) - def closeTab(self, index): - w = self.tab.widget(index) - self.tab.removeTab(index) - w.setCurrentModelIndex(QModelIndex()) - w.deleteLater() - def closeAllTabs(self): - while(self.tab.count()): - self.closeTab(0) + for ts in self.allTabSplitters(): + while(ts.tab.count()): + ts.closeTab(0) + + for ts in reversed(self.allTabSplitters()): + ts.closeSplit() + + def allTabs(self, tabWidget=None): + """Returns all the tabs from the given tabWidget. If tabWidget is None, from the current tabWidget.""" + if tabWidget is None: + tabWidget = self.currentTabWidget() + return [tabWidget.widget(i) for i in range(tabWidget.count())] + + def allAllTabs(self): + """Returns a list of all tabs, from all tabWidgets.""" + r = [] + for ts in self.allTabSplitters(): + r.extend(self.allTabs(ts.tab)) + return r + + def allTabSplitters(self): + r = [] + ts = self.tabSplitter + while ts: + r.append(ts) + ts = ts.secondTab + return r - def allTabs(self): - return [self.tab.widget(i) for i in range(self.tab.count())] ############################################################################### # SELECTION AND UPDATES @@ -126,20 +141,20 @@ class mainEditor(QWidget, Ui_mainEditor): # Checking if tab is already openned for w in self.allTabs(): if w.currentIndex == index: - self.tab.setCurrentWidget(w) + self.currentTabWidget().setCurrentWidget(w) return if qApp.keyboardModifiers() & Qt.ControlModifier: newTab = True - if newTab or not self.tab.count(): + if newTab or not self.currentTabWidget().count(): editor = editorWidget(self) editor.setCurrentModelIndex(index) - self.tab.addTab(editor, title) - self.tab.setCurrentIndex(self.tab.count() - 1) + self.currentTabWidget().addTab(editor, title) + self.currentTabWidget().setCurrentIndex(self.currentTabWidget().count() - 1) else: self.currentEditor().setCurrentModelIndex(index) - self.tab.setTabText(self.tab.currentIndex(), title) + self.currentTabWidget().setTabText(self.currentTabWidget().currentIndex(), title) ############################################################################### # UI @@ -157,7 +172,7 @@ class mainEditor(QWidget, Ui_mainEditor): self.btnRedacFolderText.setVisible(visible) self.btnRedacFolderCork.setVisible(visible) self.btnRedacFolderOutline.setVisible(visible) - self.sldCorkSizeFactor.setVisible(visible) + self.sldCorkSizeFactor.setVisible(visible and self.btnRedacFolderCork.isChecked()) self.btnRedacFullscreen.setVisible(not visible) def updateFolderViewButtons(self, view): @@ -216,20 +231,20 @@ class mainEditor(QWidget, Ui_mainEditor): self.currentEditor().setFolderView(view) def setCorkSizeFactor(self, val): - for w in self.allTabs(): + for w in self.allAllTabs(): w.setCorkSizeFactor(val) settings.corkSizeFactor = val def updateCorkView(self): - for w in self.allTabs(): + for w in self.allAllTabs(): w.corkView.viewport().update() def updateCorkBackground(self): - for w in self.allTabs(): + for w in self.allAllTabs(): w.corkView.updateBackground() def updateTreeView(self): - for w in self.allTabs(): + for w in self.allAllTabs(): w.outlineView.viewport().update() def showFullScreen(self): @@ -242,9 +257,9 @@ class mainEditor(QWidget, Ui_mainEditor): def setDict(self, dict): print(dict) - for w in self.allTabs(): + for w in self.allAllTabs(): w.setDict(dict) def toggleSpellcheck(self, val): - for w in self.allTabs(): + for w in self.allAllTabs(): w.toggleSpellcheck(val) diff --git a/manuskript/ui/editors/mainEditor_ui.py b/manuskript/ui/editors/mainEditor_ui.py index dd617fd5..2f44874b 100644 --- a/manuskript/ui/editors/mainEditor_ui.py +++ b/manuskript/ui/editors/mainEditor_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'manuskript/ui/editors/mainEditor_ui.ui' # -# Created: Fri Apr 8 18:15:49 2016 +# Created: Sun Apr 10 11:19:00 2016 # by: PyQt5 UI code generator 5.2.1 # # WARNING! All changes made in this file will be lost! @@ -14,15 +14,17 @@ class Ui_mainEditor(object): mainEditor.setObjectName("mainEditor") mainEditor.resize(791, 319) self.verticalLayout = QtWidgets.QVBoxLayout(mainEditor) + self.verticalLayout.setSpacing(0) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") - self.tab = QtWidgets.QTabWidget(mainEditor) - self.tab.setDocumentMode(True) - self.tab.setTabsClosable(True) - self.tab.setMovable(True) - self.tab.setProperty("tabBarAutoHide", False) - self.tab.setObjectName("tab") - self.verticalLayout.addWidget(self.tab) + self.tabSplitter = tabSplitter(mainEditor) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.tabSplitter.sizePolicy().hasHeightForWidth()) + self.tabSplitter.setSizePolicy(sizePolicy) + self.tabSplitter.setObjectName("tabSplitter") + self.verticalLayout.addWidget(self.tabSplitter) self.horizontalLayout_19 = QtWidgets.QHBoxLayout() self.horizontalLayout_19.setObjectName("horizontalLayout_19") self.btnRedacFolderText = QtWidgets.QPushButton(mainEditor) @@ -89,7 +91,6 @@ class Ui_mainEditor(object): self.verticalLayout.addLayout(self.horizontalLayout_19) self.retranslateUi(mainEditor) - self.tab.setCurrentIndex(-1) QtCore.QMetaObject.connectSlotsByName(mainEditor) def retranslateUi(self, mainEditor): @@ -100,4 +101,5 @@ class Ui_mainEditor(object): self.btnRedacFolderOutline.setText(_translate("mainEditor", "Outline")) self.btnRedacFullscreen.setShortcut(_translate("mainEditor", "F11")) +from manuskript.ui.editors.tabSplitter import tabSplitter from manuskript.ui.editors.textFormat import textFormat diff --git a/manuskript/ui/editors/mainEditor_ui.ui b/manuskript/ui/editors/mainEditor_ui.ui index cfdc3b55..726bad9e 100644 --- a/manuskript/ui/editors/mainEditor_ui.ui +++ b/manuskript/ui/editors/mainEditor_ui.ui @@ -14,6 +14,9 @@ Form + + 0 + 0 @@ -27,21 +30,12 @@ 0 - - - -1 - - - true - - - true - - - true - - - false + + + + 0 + 0 + @@ -228,6 +222,12 @@
manuskript.ui.editors.textFormat.h
1 + + tabSplitter + QWidget +
manuskript.ui.editors.tabSplitter.h
+ 1 +
diff --git a/manuskript/ui/editors/tabSplitter.py b/manuskript/ui/editors/tabSplitter.py new file mode 100644 index 00000000..8e4d34ec --- /dev/null +++ b/manuskript/ui/editors/tabSplitter.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# --!-- coding: utf8 --!-- +import locale + +from PyQt5.QtCore import QModelIndex, QRect, QPoint +from PyQt5.QtCore import Qt, QObject, QSize +from PyQt5.QtGui import QPixmap, QPainter +from PyQt5.QtWidgets import QWidget, QPushButton, qApp + +from manuskript.functions import mainWindow +from manuskript.ui import style +from manuskript.ui.editors.tabSplitter_ui import Ui_tabSplitter + + +class tabSplitter(QWidget, Ui_tabSplitter): + def __init__(self, parent=None, mainEditor=None): + QWidget.__init__(self, parent) + self.setupUi(self) + self.tab.setStyleSheet(style.mainEditorTabSS()) + + try: + self.tab.setTabBarAutoHide(True) + except AttributeError: + print("Info: install Qt 5.4 or higher to use tabbar auto-hide in editor.") + + #Remove empty widget + # self.splitter.widget(1).setParent(None) + + self.btmSplit = QPushButton() + + self.btnSplit = QPushButton("S", self) + self.btnSplit.setGeometry(QRect(0, 0, 24, 24)) + self.btnSplit.setMinimumSize(QSize(24, 24)) + self.btnSplit.setMaximumSize(QSize(24, 24)) + self.btnSplit.setCheckable(True) + self.btnSplit.setFlat(True) + self.btnSplit.setObjectName("btnSplit") + + self.mainEditor = mainEditor or parent + + self.btnSplit.clicked.connect(self.split) + self.secondTab = None + self.splitState = 0 + self.focusTab = 1 + + self.tab.tabCloseRequested.connect(self.closeTab) + self.tab.currentChanged.connect(self.mainEditor.tabChanged) + + qApp.focusChanged.connect(self.focusChanged) + + ############################################################################### + # TABS + ############################################################################### + + def closeTab(self, index): + w = self.tab.widget(index) + self.tab.removeTab(index) + w.setCurrentModelIndex(QModelIndex()) + w.deleteLater() + + def tabOpenIndexes(self): + sel = [] + for i in range(self.tab.count()): + sel.append(mainWindow().mdlOutline.ID(self.tab.widget(i).currentIndex)) + return sel + + def openIndexes(self): + r = [ + self.splitState, + self.tabOpenIndexes(), + self.secondTab.openIndexes() if self.secondTab else None, + ] + return r + + def restoreOpenIndexes(self, openIndexes): + + try: + + self.split(state=openIndexes[0]) + + for i in openIndexes[1]: + idx = mainWindow().mdlOutline.getIndexByID(i) + self.mainEditor.setCurrentModelIndex(idx, newTab=True) + + if openIndexes[2]: + self.focusTab = 2 + self.secondTab.restoreOpenIndexes(openIndexes[2]) + + except: + + print("Failed to load indexes from settings...") + print("Indexes:", openIndexes) + + ############################################################################### + # SPLITTER + ############################################################################### + + def split(self, toggled=None, state=None): + + if state is None and self.splitState == 0 or state == 1: + if self.secondTab is None: + self.addSecondTab() + + self.splitState = 1 + self.splitter.setOrientation(Qt.Horizontal) + self.btnSplit.setChecked(True) + + elif state is None and self.splitState == 1 or state == 2: + if self.secondTab is None: + self.addSecondTab() + + self.splitter.setOrientation(Qt.Vertical) + self.splitState = 2 + self.btnSplit.setChecked(True) + + else: + self.closeSplit() + + def addSecondTab(self): + self.secondTab = tabSplitter(mainEditor=self.mainEditor) + self.splitter.addWidget(self.secondTab) + self.splitter.setStretchFactor(0, 10) + self.splitter.setStretchFactor(1, 10) + + if self.mainEditor.currentEditor(): + idx = self.mainEditor.currentEditor().currentIndex + self.focusTab = 2 + self.mainEditor.setCurrentModelIndex(idx) + + def closeSplit(self): + if self.secondTab: + self.secondTab.setParent(None) + self.secondTab.deleteLater() + qApp.focusChanged.disconnect(self.secondTab.focusChanged) + self.focusTab = 1 + self.secondTab = None + self.btnSplit.setChecked(False) + self.splitState = 0 + + # def resizeEvent(self, event): + # r = self.geometry() + # r.moveLeft(0) + # r.moveTop(0) + # self.splitter.setGeometry(r) + # self.btnSplit.setGeometry(QRect(0, 0, 24, 24)) + + def focusChanged(self, old, new): + if self.secondTab is None or new is None: + return + + oldFT = self.focusTab + while new: + if new == self.tab: + self.focusTab = 1 + new = None + elif new == self.secondTab: + self.focusTab = 2 + new = None + else: + new = new.parent() + + if self.focusTab != oldFT: + self.mainEditor.tabChanged() \ No newline at end of file diff --git a/manuskript/ui/editors/tabSplitter_ui.py b/manuskript/ui/editors/tabSplitter_ui.py new file mode 100644 index 00000000..3b20b1d5 --- /dev/null +++ b/manuskript/ui/editors/tabSplitter_ui.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'manuskript/ui/editors/tabSplitter_ui.ui' +# +# Created: Sun Apr 10 16:27:03 2016 +# by: PyQt5 UI code generator 5.2.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_tabSplitter(object): + def setupUi(self, tabSplitter): + tabSplitter.setObjectName("tabSplitter") + tabSplitter.resize(400, 300) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(tabSplitter.sizePolicy().hasHeightForWidth()) + tabSplitter.setSizePolicy(sizePolicy) + tabSplitter.setWindowTitle("tabSPlitter") + self.verticalLayout = QtWidgets.QVBoxLayout(tabSplitter) + self.verticalLayout.setSpacing(0) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setObjectName("verticalLayout") + self.splitter = QtWidgets.QSplitter(tabSplitter) + self.splitter.setMinimumSize(QtCore.QSize(30, 30)) + self.splitter.setOrientation(QtCore.Qt.Horizontal) + self.splitter.setObjectName("splitter") + self.tab = QtWidgets.QTabWidget(self.splitter) + self.tab.setTabsClosable(True) + self.tab.setMovable(True) + self.tab.setObjectName("tab") + self.verticalLayout.addWidget(self.splitter) + + self.retranslateUi(tabSplitter) + QtCore.QMetaObject.connectSlotsByName(tabSplitter) + + def retranslateUi(self, tabSplitter): + pass + diff --git a/manuskript/ui/editors/tabSplitter_ui.ui b/manuskript/ui/editors/tabSplitter_ui.ui new file mode 100644 index 00000000..8df060a1 --- /dev/null +++ b/manuskript/ui/editors/tabSplitter_ui.ui @@ -0,0 +1,63 @@ + + + tabSplitter + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + tabSPlitter + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 30 + 30 + + + + Qt::Horizontal + + + + true + + + true + + + + + + + + + diff --git a/manuskript/ui/style.py b/manuskript/ui/style.py index 9a3daed4..ca3f48db 100644 --- a/manuskript/ui/style.py +++ b/manuskript/ui/style.py @@ -90,7 +90,7 @@ def mainEditorTabSS(): border: 1px solid #999; } QTabWidget::tab-bar{ - left:10px; + left:25px; } QTabBar{ background: transparent; @@ -113,6 +113,29 @@ def mainEditorTabSS(): QTabBar::tab:!selected:hover{ background:#ddd; } + +QScrollBar:vertical { + border: none; + background: transparent; + width: 10px; +} +QScrollBar::handle { + background: rgba(180, 180, 180, 40%); +} +QScrollBar::add-line:vertical { + width:0; + height: 0; + border: none; + background: none; +} + +QScrollBar::sub-line:vertical { + width:0; + height: 0; + border: none; + background: none; +} + """ def toolBarSS(): diff --git a/manuskript/ui/views/corkView.py b/manuskript/ui/views/corkView.py index 305cd26e..091bafda 100644 --- a/manuskript/ui/views/corkView.py +++ b/manuskript/ui/views/corkView.py @@ -29,6 +29,7 @@ class corkView(QListView, dndView, outlineBasics): self.setStyleSheet("""QListView {{ background:{color}; background-image: url({url}); + background-attachment: fixed; }}""".format( color=settings.corkBackground["color"], url=img