Show list of recent files on welcome screen

This commit is contained in:
Rebecca Breu 2021-09-29 21:02:35 +02:00
parent 05301859b6
commit 22e219f56f
5 changed files with 206 additions and 53 deletions

View file

@ -1,6 +1,16 @@
0.2.1 - (unreleased)
====================
Added
-----
* Show list of recent files on welcome screen
Fixed
-----
* Various typos (by luzpaz)
0.2.0 - 2021-09-06
==================

View file

@ -16,33 +16,111 @@
import logging
import os.path
from PyQt6 import QtCore, QtWidgets
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6.QtCore import Qt
from beeref import constants
from beeref.config import logfile_name
from beeref.config import logfile_name, BeeSettings
from beeref.main_controls import MainControlsMixin
logger = logging.getLogger(__name__)
class WelcomeOverlay(QtWidgets.QWidget):
class RecentFilesModel(QtCore.QAbstractListModel):
"""An entry in the 'Recent Files' list."""
def __init__(self, files):
super().__init__()
self.files = files
def rowCount(self, parent):
return len(self.files)
def data(self, index, role):
if role == QtCore.Qt.ItemDataRole.DisplayRole:
return os.path.basename(self.files[index.row()])
if role == QtCore.Qt.ItemDataRole.FontRole:
font = QtGui.QFont()
font.setUnderline(True)
return font
class RecentFilesView(QtWidgets.QListView):
def __init__(self, parent, files=None):
super().__init__(parent)
self.files = files or []
self.clicked.connect(self.on_clicked)
self.setModel(RecentFilesModel(self.files))
self.setMouseTracking(True)
def on_clicked(self, index):
self.parent().parent().open_from_file(self.files[index.row()])
def update_files(self, files):
self.files = files
self.model().files = files
self.reset()
def sizeHint(self):
size = QtCore.QSize()
height = sum(
(self.sizeHintForRow(i) + 2) for i in range(len(self.files)))
width = max(self.sizeHintForColumn(i) for i in range(len(self.files)))
size.setHeight(height)
size.setWidth(width + 2)
return size
def mouseMoveEvent(self, event):
index = self.indexAt(
QtCore.QPoint(int(event.position().x()),
int(event.position().y())))
if index.isValid():
self.setCursor(Qt.CursorShape.PointingHandCursor)
else:
self.setCursor(Qt.CursorShape.ArrowCursor)
super().mouseMoveEvent(event)
class WelcomeOverlay(MainControlsMixin, QtWidgets.QWidget):
"""Some basic info to be displayed when the scene is empty."""
txt = """<p>Paste or drop images here.</p>
<p>Right-click for more options.</p>"""
def __init__(self, parent=None):
def __init__(self, parent):
super().__init__(parent)
self.control_target = parent
self.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground)
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
label = QtWidgets.QLabel(self)
label.setText(self.txt)
self.init_main_controls()
# Recent files
self.files_layout = QtWidgets.QVBoxLayout()
self.files_layout.addStretch(50)
self.files_layout.addWidget(
QtWidgets.QLabel('<h3>Recent Files</h3>', self))
self.files_view = RecentFilesView(self)
self.files_layout.addWidget(self.files_view)
self.files_layout.addStretch(50)
# Help text
label = QtWidgets.QLabel(self.txt, self)
label.setAlignment(Qt.AlignmentFlag.AlignVCenter
| Qt.AlignmentFlag.AlignCenter)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
self.layout = QtWidgets.QHBoxLayout()
self.layout.addStretch(50)
self.layout.addWidget(label)
self.layout.addStretch(50)
self.setLayout(self.layout)
def show(self):
files = BeeSettings().get_recent_files(existing_only=True)
self.files_view.update_files(files)
if files and self.layout.indexOf(self.files_layout) < 0:
self.layout.insertLayout(0, self.files_layout)
super().show()
class BeeProgressDialog(QtWidgets.QProgressDialog):

78
beeref/main_controls.py Normal file
View file

@ -0,0 +1,78 @@
# This file is part of BeeRef.
#
# BeeRef is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BeeRef is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BeeRef. If not, see <https://www.gnu.org/licenses/>.
import logging
from PyQt6 import QtCore, QtGui
from PyQt6.QtCore import Qt
from beeref import commands
from beeref.items import BeePixmapItem
from beeref import fileio
logger = logging.getLogger(__name__)
class MainControlsMixin:
"""Basic controls shared by the main view and the welcome overlay:
* Right-click menu
* Dropping files
"""
def init_main_controls(self):
self.setContextMenuPolicy(
Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(
self.control_target.on_context_menu)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
mimedata = event.mimeData()
logger.debug(f'Drag enter event: {mimedata.formats()}')
if mimedata.hasUrls():
event.acceptProposedAction()
elif mimedata.hasImage():
event.acceptProposedAction()
else:
logger.info('Attempted drop not an image')
def dragMoveEvent(self, event):
event.acceptProposedAction()
def dropEvent(self, event):
mimedata = event.mimeData()
logger.debug(f'Handling file drop: {mimedata.formats()}')
pos = QtCore.QPoint(round(event.position().x()),
round(event.position().y()))
if mimedata.hasUrls():
logger.debug(f'Found dropped urls: {mimedata.urls()}')
if not self.control_target.scene.items():
# Check if we have a bee file we can open directly
path = mimedata.urls()[0]
if (path.isLocalFile()
and fileio.is_bee_file(path.toLocalFile())):
self.control_target.open_from_file(path.toLocalFile())
return
self.control_target.do_insert_images(mimedata.urls(), pos)
elif mimedata.hasImage():
img = QtGui.QImage(mimedata.imageData())
item = BeePixmapItem(img)
pos = self.control_target.mapToScene(pos)
self.control_target.undo_stack.push(
commands.InsertItems(self.control_target.scene, [item], pos))
else:
logger.info('Drop not an image')

View file

@ -28,6 +28,7 @@ from beeref import constants
from beeref import fileio
from beeref import gui
from beeref.items import BeePixmapItem, BeeTextItem
from beeref.main_controls import MainControlsMixin
from beeref.scene import BeeGraphicsScene
@ -35,7 +36,9 @@ commandline_args = CommandlineArgs()
logger = logging.getLogger(__name__)
class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
class BeeGraphicsView(MainControlsMixin,
QtWidgets.QGraphicsView,
ActionsMixin):
def __init__(self, app, parent=None):
super().__init__(parent)
@ -47,7 +50,6 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
self.setBackgroundBrush(
QtGui.QBrush(QtGui.QColor(*constants.COLORS['Scene:Canvas'])))
self.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)
self.setAcceptDrops(True)
self.undo_stack = QtGui.QUndoStack(self)
self.undo_stack.setUndoLimit(100)
@ -68,9 +70,8 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
# Context menu and actions
self.build_menu_and_actions()
self.setContextMenuPolicy(
Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self.on_context_menu)
self.control_target = self
self.init_main_controls()
# Load file given via command line
if commandline_args.filename:
@ -671,39 +672,3 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin):
super().resizeEvent(event)
self.recalc_scene_rect()
self.welcome_overlay.resize(self.size())
def dragEnterEvent(self, event):
mimedata = event.mimeData()
logger.debug(f'Drag enter event: {mimedata.formats()}')
if mimedata.hasUrls():
event.acceptProposedAction()
elif mimedata.hasImage():
event.acceptProposedAction()
else:
logger.info('Attempted drop not an image')
def dragMoveEvent(self, event):
event.acceptProposedAction()
def dropEvent(self, event):
mimedata = event.mimeData()
logger.debug(f'Handling file drop: {mimedata.formats()}')
pos = QtCore.QPoint(round(event.position().x()),
round(event.position().y()))
if mimedata.hasUrls():
logger.debug(f'Found dropped urls: {mimedata.urls()}')
if not self.scene.items():
# Check if we have a bee file we can open directly
path = mimedata.urls()[0]
if (path.isLocalFile()
and fileio.is_bee_file(path.toLocalFile())):
self.open_from_file(path.toLocalFile())
return
self.do_insert_images(mimedata.urls(), pos)
elif mimedata.hasImage():
img = QtGui.QImage(mimedata.imageData())
item = BeePixmapItem(img)
pos = self.mapToScene(pos)
self.undo_stack.push(commands.InsertItems(self.scene, [item], pos))
else:
logger.info('Drop not an image')

View file

@ -1,8 +1,10 @@
from PyQt6 import QtWidgets
from unittest.mock import MagicMock
from PyQt6 import QtCore, QtWidgets
from PyQt6.QtCore import Qt
from beeref.config import logfile_name
from beeref.gui import DebugLogDialog
from beeref.gui import DebugLogDialog, RecentFilesModel
def test_debug_log_dialog(qtbot, settings, view):
@ -16,3 +18,23 @@ def test_debug_log_dialog(qtbot, settings, view):
qtbot.mouseClick(dialog.copy_button, Qt.MouseButton.LeftButton)
clipboard = QtWidgets.QApplication.clipboard()
assert clipboard.text() == 'my log output'
def test_recent_files_model_rowcount(view):
model = RecentFilesModel(['foo.png', 'bar.png'])
assert model.rowCount(None) == 2
def test_recent_files_model_data_diplayrole(view):
model = RecentFilesModel(['foo.png', 'bar.png'])
index = MagicMock()
index.row.return_value = 1
assert model.data(index, QtCore.Qt.ItemDataRole.DisplayRole) == 'bar.png'
def test_recent_files_model_data_fontrole(view):
model = RecentFilesModel(['foo.png', 'bar.png'])
index = MagicMock()
index.row.return_value = 1
font = model.data(index, QtCore.Qt.ItemDataRole.FontRole)
assert font.underline() is True