diff --git a/src/windows/main_window.py b/src/windows/main_window.py index b7cb83c103..22d56a1220 100644 --- a/src/windows/main_window.py +++ b/src/windows/main_window.py @@ -2130,17 +2130,37 @@ def foundCurrentVersion(self, version): def moveEvent(self, event): """ Move tutorial dialogs also (if any)""" + QMainWindow.moveEvent(self, event) if self.tutorial_manager: + #log.info("Sending move event to tutorial manager") self.tutorial_manager.re_position_dialog() - def eventFilter(self, object, e): - """ Filter out certain types of window events """ - if e.type() == QEvent.WindowActivate: - self.tutorial_manager.re_show_dialog() - elif e.type() == QEvent.WindowStateChange and self.isMinimized(): - self.tutorial_manager.minimize() + def resizeEvent(self, event): + QMainWindow.resizeEvent(self, event) + if self.tutorial_manager: + #log.info("Sending resize event to tutorial manager") + self.tutorial_manager.re_position_dialog() + + def showEvent(self, event): + """ Have any child windows follow main-window state """ + #log.info("Showing main window") + QMainWindow.showEvent(self, event) + for child in self.findChildren(QDockWidget): + if child.isFloating() and child.isEnabled(): + # child.setWindowState(self.windowState()) + #log.info("Showing child {}".format(child.windowTitle())) + child.raise_() + child.show() + + def hideEvent(self, event): + """ Have any child windows hide with main window """ + #log.info("Hiding main window") + QMainWindow.hideEvent(self, event) + for child in self.findChildren(QDockWidget): + if child.isFloating() and child.isVisible(): + #log.info("Hiding child {}".format(child.windowTitle())) + child.hide() - return False def show_property_timeout(self): """Callback for show property timer""" @@ -2393,9 +2413,6 @@ def __init__(self, mode=None): self.ExportFrame.connect(self.FrameExported) self.ExportEnded.connect(self.ExportFinished) - # Install event filter - self.installEventFilter(self) - # Save settings s.save() diff --git a/src/windows/ui/main-window.ui b/src/windows/ui/main-window.ui index c99067e492..9456c345e7 100644 --- a/src/windows/ui/main-window.ui +++ b/src/windows/ui/main-window.ui @@ -6,8 +6,8 @@ 0 0 - 808 - 631 + 1200 + 800 @@ -190,9 +190,18 @@ - - 9 - + + 9 + + + 9 + + + 9 + + + 9 + @@ -214,9 +223,18 @@ - - 9 - + + 9 + + + 9 + + + 9 + + + 9 + @@ -232,9 +250,18 @@ - - 9 - + + 9 + + + 9 + + + 9 + + + 9 + @@ -250,9 +277,18 @@ - - 9 - + + 9 + + + 9 + + + 9 + + + 9 + @@ -274,9 +310,18 @@ - - 9 - + + 9 + + + 9 + + + 9 + + + 9 + @@ -293,6 +338,93 @@ + + + false + + + + 0 + 0 + + + + + 10 + 10 + + + + ForbiddenCursor + + + Qt::ClickFocus + + + Qt::NoContextMenu + + + true + + + false + + + true + + + QDockWidget::DockWidgetFloatable + + + Qt::NoDockWidgetArea + + + Tutorial + + + false + + + false + + + false + + + 0 + + + + Qt::ClickFocus + + + Qt::NoContextMenu + + + false + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + @@ -779,6 +911,39 @@ Clear + + + true + + + Show All + + + Show All + + + + + true + + + Video + + + Video + + + + + true + + + Audio + + + Audio + + diff --git a/src/windows/views/tutorial.py b/src/windows/views/tutorial.py index 4db4569153..2537d0aac6 100644 --- a/src/windows/views/tutorial.py +++ b/src/windows/views/tutorial.py @@ -29,7 +29,7 @@ from PyQt5.QtCore import Qt, QPoint, QRectF, QEvent from PyQt5.QtGui import * -from PyQt5.QtWidgets import QLabel, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QToolButton, QCheckBox +from PyQt5.QtWidgets import QLabel, QWidget, QDockWidget, QVBoxLayout, QHBoxLayout, QPushButton, QToolButton, QCheckBox from classes.logger import log from classes.settings import get_settings @@ -38,26 +38,27 @@ class TutorialDialog(QWidget): - """ A QWidget used to instruct a user how to use a certain feature """ + """ A customized QWidget used to instruct a user how to use a certain feature """ def paintEvent(self, event, *args): """ Custom paint event """ # Paint custom frame image on QWidget painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) + frameColor = QColor("#53a0ed") # Paint blue rounded rectangle path = QPainterPath() path.addRoundedRect(QRectF(31, 0, self.width()-31, self.height()), 10, 10) painter.setPen(Qt.NoPen) - painter.fillPath(path, QColor("#53a0ed")) + painter.fillPath(path, frameColor) painter.drawPath(path) # Paint gray rounded rectangle path = QPainterPath() path.addRoundedRect(QRectF(32, 1, self.width()-33, self.height()-2), 10, 10) painter.setPen(Qt.NoPen) - painter.fillPath(path, QColor("#424242")) + painter.fillPath(path, self.palette().color(QPalette.Window)) painter.drawPath(path) # Paint blue triangle (if needed) @@ -68,29 +69,9 @@ def paintEvent(self, event, *args): path.lineTo (31, 35 - arrow_height) path.lineTo (31, (35 - arrow_height) + (arrow_height * 2)) path.lineTo (0, 35) - painter.fillPath(path, QColor("#53a0ed")) + painter.fillPath(path, frameColor) painter.drawPath(path) - def eventFilter(self, object, e): - if e.type() == QEvent.WindowActivate: - # Raise parent window, and then this tutorial - #log.info("Raising main app and tutorial popup") - get_app().window.show() - get_app().window.raise_() - self.moveWidget() - self.show() - self.raise_() - # Filter event out (prevent further handling) - return True - else: - return False - - def moveWidget(self): - """ Move widget next to its position widget """ - x = self.position_widget.mapToGlobal(self.position_widget.pos()).x() - y = self.position_widget.mapToGlobal(self.position_widget.pos()).y() - self.move(QPoint(x + self.x_offset, y + self.y_offset)) - def checkbox_metrics_callback(self, state): """ Callback for error and anonymous usage checkbox""" s = get_settings() @@ -107,7 +88,7 @@ def checkbox_metrics_callback(self, state): # Disable metric sending s.set("send_metrics", False) - def __init__(self, id, text, position_widget, x_offset, y_offset, arrow, *args): + def __init__(self, id, text, arrow, *args): # Invoke parent init QWidget.__init__(self, *args) @@ -117,9 +98,6 @@ def __init__(self, id, text, position_widget, x_offset, y_offset, arrow, *args): # Keep track of widget to position next to self.id = id - self.position_widget = position_widget - self.x_offset = x_offset - self.y_offset = y_offset self.arrow = arrow # Create vertical box @@ -173,17 +151,10 @@ def __init__(self, id, text, position_widget, x_offset, y_offset, arrow, *args): self.setMinimumWidth(350) self.setMinimumHeight(100) - # Make it's own window - self.setWindowTitle("Tutorial") - self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint) + # Make transparent + self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAttribute(Qt.WA_TranslucentBackground, True) - self.setFocusPolicy(Qt.ClickFocus) - - # Position window next to other widget - self.moveWidget() - - # Install event filter - self.installEventFilter(self) + #self.setWindowFlags(Qt.FramelessWindowHint) class TutorialManager(object): @@ -191,11 +162,12 @@ class TutorialManager(object): def process(self, parent_name=None): """ Process and show the first non-completed tutorial """ - log.info("process tutorial dialogs") # Do nothing if a tutorial is already visible if self.current_dialog: - self.re_show_dialog() + # XXX: Respond to possible dock floats/moves + self.dock.raise_() + self.re_position_dialog() return # Loop through and add each tutorial dialog @@ -216,15 +188,25 @@ def process(self, parent_name=None): continue # Create tutorial - tutorial_dialog = TutorialDialog(tutorial_id, tutorial_text, tutorial_object, tutorial_x_offset, tutorial_y_offset, turorial_arrow) + self.position_widget = tutorial_object + self.x_offset = tutorial_x_offset + self.y_offset = tutorial_y_offset + tutorial_dialog = TutorialDialog(tutorial_id, tutorial_text, turorial_arrow) # Connect signals tutorial_dialog.btn_next_tip.clicked.connect(functools.partial(self.next_tip, tutorial_id)) tutorial_dialog.btn_close_tips.clicked.connect(functools.partial(self.hide_tips, tutorial_id, True)) - # Show dialog + # Insert into tutorial dock + self.dock.setWidget(tutorial_dialog) self.current_dialog = tutorial_dialog - self.current_dialog.show() + + # Show dialog + self.dock.adjustSize() + self.dock.setEnabled(True) + self.re_position_dialog() + #self.current_dialog.show() + self.dock.show() break def get_object(self, object_id): @@ -250,17 +232,14 @@ def get_object(self, object_id): def next_tip(self, tid): """ Mark the current tip completed, and show the next one """ - log.info("next_tip") - # Hide matching tutorial self.hide_tips(tid) - # Process the next type + # Advance to the next one self.process() def hide_tips(self, tid, user_clicked=False): """ Hide the current tip, and don't show anymore """ - log.info("hide_tips") s = get_settings() # Loop through and find current tid @@ -284,7 +263,8 @@ def hide_tips(self, tid, user_clicked=False): def close_dialogs(self): """ Close any open tutorial dialogs """ if self.current_dialog: - self.current_dialog.hide() + self.dock.hide() + self.dock.setEnabled(False) self.current_dialog = None def exit_manager(self): @@ -305,24 +285,21 @@ def exit_manager(self): def re_show_dialog(self): """ Re show an active dialog """ if self.current_dialog: - self.current_dialog.showNormal() - self.current_dialog.raise_() + self.dock.raise_() + self.dock.show() def re_position_dialog(self): """ Reposition a tutorial dialog next to another widget """ if self.current_dialog: - self.current_dialog.moveWidget() - - def minimize(self): - """ Minimize any visible tutorial dialog """ - log.info("minimize tutorial") - if self.current_dialog: - self.current_dialog.showMinimized() + """ Move widget next to its position widget """ + x = self.position_widget.mapToGlobal(self.position_widget.pos()).x() + y = self.position_widget.mapToGlobal(self.position_widget.pos()).y() + self.dock.move(QPoint(x + self.x_offset, y + self.y_offset)) def __init__(self, win): """ Constructor """ - self.tutorials = [] self.win = win + self.dock = win.dockTutorial self.current_dialog = None # get translations @@ -338,14 +315,22 @@ def __init__(self, win): self.tutorial_objects = [ {"id":"0", "x":400, "y":0, "object_id":"filesTreeView", "text":_("Welcome! OpenShot Video Editor is an award-winning, open-source video editing application! This tutorial will walk you through the basics.

Would you like to automatically send errors and metrics to help improve OpenShot?"), "arrow":False}, {"id":"1", "x":20, "y":0, "object_id":"filesTreeView", "text":_("Project Files: Get started with your project by adding video, audio, and image files here. Drag and drop files from your file system."), "arrow":True}, {"id":"2", "x":200, "y":-15, "object_id":"timeline", "text":_("Timeline: Arrange your clips on the timeline here. Overlap clips to create automatic transitions. Access lots of fun presets and options by right-clicking on clips."), "arrow":True}, - {"id":"3", "x":150, "y":100, "object_id":"dockVideoContents", "text":_("Video Preview: Watch your timeline video preview here. Use the buttons (play, rewind, fast-forward) to control the video playback."), "arrow":True}, + {"id":"3", "x":200, "y":100, "object_id":"dockVideoContents", "text":_("Video Preview: Watch your timeline video preview here. Use the buttons (play, rewind, fast-forward) to control the video playback."), "arrow":True}, {"id":"4", "x":20, "y":-35, "object_id":"propertyTableView", "text":_("Properties: View and change advanced properties of clips and effects here. Right-clicking on clips is usually faster than manually changing properties."), "arrow":True}, {"id":"5", "x":20, "y":10, "object_id":"transitionsTreeView", "text":_("Transitions: Create a gradual fade from one clip to another. Drag and drop a transition onto the timeline and position it on top of a clip (usually at the beginning or ending)."), "arrow":True}, {"id":"6", "x":20, "y":20, "object_id":"effectsTreeView", "text":_("Effects: Adjust brightness, contrast, saturation, and add exciting special effects. Drag and drop an effect onto the timeline and position it on top of a clip (or track)"), "arrow":True}, {"id":"7", "x":-265, "y":-22, "object_id":"export_button", "text":_("Export Video: When you are ready to create your finished video, click this button to export your timeline as a single video file."), "arrow":True} ] - # Connect to dock widgets + # Configure tutorial frame + self.dock.setTitleBarWidget(QWidget()) # Prevents window decoration + self.dock.setAttribute(Qt.WA_NoSystemBackground, True) + self.dock.setAttribute(Qt.WA_TranslucentBackground, True) + self.dock.setWindowFlags(Qt.FramelessWindowHint) + self.dock.setFloating(True) + + + # Connect to interface dock widgets self.win.dockFiles.visibilityChanged.connect(functools.partial(self.process, "dockFiles")) self.win.dockTransitions.visibilityChanged.connect(functools.partial(self.process, "dockTransitions")) self.win.dockEffects.visibilityChanged.connect(functools.partial(self.process, "dockEffects"))