263 lines
9.2 KiB
Plaintext
263 lines
9.2 KiB
Plaintext
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
|
|
|
/*!
|
|
\example tools/undoframework
|
|
\title Undo Framework Example
|
|
\examplecategory {Data Processing & I/O}
|
|
\ingroup examples-widgets-tools
|
|
|
|
\brief This example shows how to implement undo/redo functionality
|
|
with the Qt undo framework.
|
|
|
|
\image undoframeworkexample.png The Undo Diagram Example
|
|
|
|
In the Qt undo framework, all actions that the user performs are
|
|
implemented in classes that inherit QUndoCommand. An undo command
|
|
class knows how to both \l{QUndoCommand::}{redo()} - or just do
|
|
the first time - and \l{QUndoCommand::}{undo()} an action. For
|
|
each action the user performs, a command is placed on a
|
|
QUndoStack. Since the stack contains all commands executed
|
|
(stacked in chronological order) on the document, it can roll the
|
|
state of the document backwards and forwards by undoing and redoing
|
|
its commands. See the \l{Overview of Qt's Undo Framework}{overview
|
|
document} for a high-level introduction to the undo framework.
|
|
|
|
The undo example implements a simple diagram application. It is
|
|
possible to add and delete items, which are either box or
|
|
rectangular shaped, and move the items by dragging them with the
|
|
mouse. The undo stack is shown in a QUndoView, which is a list in
|
|
which the commands are shown as list items. Undo and redo are
|
|
available through the edit menu. The user can also select a command
|
|
from the undo view.
|
|
|
|
We use the \l{Graphics View Framework}{graphics view
|
|
framework} to implement the diagram. We only treat the related
|
|
code briefly as the framework has examples of its own (e.g., the
|
|
\l{Diagram Scene Example}).
|
|
|
|
The example consists of the following classes:
|
|
|
|
\list
|
|
\li \c MainWindow is the main window and arranges the
|
|
example's widgets. It creates the commands based
|
|
on user input and keeps them on the command stack.
|
|
\li \c AddCommand adds an item to the scene.
|
|
\li \c DeleteCommand deletes an item from the scene.
|
|
\li \c MoveCommand when an item is moved the MoveCommand keeps record
|
|
of the start and stop positions of the move, and it
|
|
moves the item according to these when \c redo() and \c undo()
|
|
is called.
|
|
\li \c DiagramScene inherits QGraphicsScene and
|
|
emits signals for the \c MoveComands when an item is moved.
|
|
\li \c DiagramItem inherits QGraphicsPolygonItem and represents
|
|
an item in the diagram.
|
|
\endlist
|
|
|
|
\section1 MainWindow Class Definition
|
|
|
|
\snippet tools/undoframework/mainwindow.h 0
|
|
|
|
The \c MainWindow class maintains the undo stack, i.e., it creates
|
|
\l{QUndoCommand}s and pushes and pops them from the stack when it
|
|
receives the \c triggered() signal from \c undoAction and \c
|
|
redoAction.
|
|
|
|
\section1 MainWindow Class Implementation
|
|
|
|
We will start with a look at the constructor:
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 0
|
|
|
|
In the constructor, we set up the DiagramScene and QGraphicsView.
|
|
We only want \c deleteAction to be enabled when we have selected an
|
|
item, so we connect to the \c selectionChanged() signal of the scene
|
|
to \c updateActions() slot.
|
|
|
|
Here is the \c createUndoView() function:
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 1
|
|
|
|
The QUndoView is a widget that display the text, which is set with
|
|
the \l{QUndoCommand::}{setText()} function, for each QUndoCommand
|
|
in the undo stack in a list. We put it into a docking widget.
|
|
|
|
Here is the \c createActions() function:
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 2
|
|
\dots
|
|
\snippet tools/undoframework/mainwindow.cpp 5
|
|
|
|
The \c createActions() function sets up all the examples actions
|
|
in the manner shown above. The
|
|
\l{QUndoStack::}{createUndoAction()} and
|
|
\l{QUndoStack::}{createRedoAction()} methods help us create actions that
|
|
are disabled and enabled based on the state of the stack. Also,
|
|
the text of the action will be updated automatically based on the
|
|
\l{QUndoCommand::}{text()} of the undo commands. For the other
|
|
actions we have implemented slots in the \c MainWindow class.
|
|
|
|
\dots
|
|
\snippet tools/undoframework/mainwindow.cpp 6
|
|
|
|
Once all actions are created we update their state by calling the
|
|
same function that is connected to the \c selectionChanged signal of
|
|
the scene.
|
|
|
|
The \c createMenus() and \c createToolBars() functions add the
|
|
actions to menus and toolbars:
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 7
|
|
\dots
|
|
\snippet tools/undoframework/mainwindow.cpp 8
|
|
\dots
|
|
\snippet tools/undoframework/mainwindow.cpp 9
|
|
|
|
Here is the \c itemMoved() slot:
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 11
|
|
|
|
We simply push a MoveCommand on the stack, which calls \c redo()
|
|
on it.
|
|
|
|
Here is the \c deleteItem() slot:
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 12
|
|
|
|
An item must be selected to be deleted. We need to check if it is
|
|
selected as the \c deleteAction may be enabled even if an item is
|
|
not selected. This can happen as we do not catch a signal or event
|
|
when an item is selected.
|
|
|
|
Here is the \c addBox() slot:
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 13
|
|
|
|
The \c addBox() function creates an AddCommand and pushes it on
|
|
the undo stack.
|
|
|
|
Here is the \c addTriangle() sot:
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 14
|
|
|
|
The \c addTriangle() function creates an AddCommand and pushes it
|
|
on the undo stack.
|
|
|
|
Here is the implementation of \c about():
|
|
|
|
\snippet tools/undoframework/mainwindow.cpp 15
|
|
|
|
The about slot is triggered by the \c aboutAction and displays an
|
|
about box for the example.
|
|
|
|
\section1 AddCommand Class Definition
|
|
|
|
\snippet tools/undoframework/commands.h 2
|
|
|
|
The \c AddCommand class adds DiagramItem graphics items to the
|
|
DiagramScene.
|
|
|
|
\section1 AddCommand Class Implementation
|
|
|
|
We start with the constructor:
|
|
|
|
\snippet tools/undoframework/commands.cpp 7
|
|
|
|
We first create the DiagramItem to add to the DiagramScene. The
|
|
\l{QUndoCommand::}{setText()} function let us set a QString that
|
|
describes the command. We use this to get custom messages in the
|
|
QUndoView and in the menu of the main window.
|
|
|
|
\snippet tools/undoframework/commands.cpp 8
|
|
|
|
\c undo() removes the item from the scene.
|
|
|
|
\snippet tools/undoframework/commands.cpp 9
|
|
|
|
We set the position of the item as we do not do this in the
|
|
constructor.
|
|
|
|
\section1 DeleteCommand Class Definition
|
|
|
|
\snippet tools/undoframework/commands.h 1
|
|
|
|
The DeleteCommand class implements the functionality to remove an
|
|
item from the scene.
|
|
|
|
\section1 DeleteCommand Class Implementation
|
|
|
|
\snippet tools/undoframework/commands.cpp 4
|
|
|
|
We know that there must be one selected item as it is not possible
|
|
to create a DeleteCommand unless the item to be deleted is
|
|
selected and that only one item can be selected at any time.
|
|
The item must be unselected if it is inserted back into the
|
|
scene.
|
|
|
|
\snippet tools/undoframework/commands.cpp 5
|
|
|
|
The item is simply reinserted into the scene.
|
|
|
|
\snippet tools/undoframework/commands.cpp 6
|
|
|
|
The item is removed from the scene.
|
|
|
|
\section1 MoveCommand Class Definition
|
|
|
|
\snippet tools/undoframework/commands.h 0
|
|
|
|
The \l{QUndoCommand::}{mergeWith()} is reimplemented to make
|
|
consecutive moves of an item one MoveCommand, i.e, the item will
|
|
be moved back to the start position of the first move.
|
|
|
|
\section1 MoveCommand Class Implementation
|
|
|
|
|
|
The constructor of MoveCommand looks like this:
|
|
|
|
\snippet tools/undoframework/commands.cpp 0
|
|
|
|
We save both the old and new positions for undo and redo
|
|
respectively.
|
|
|
|
\snippet tools/undoframework/commands.cpp 2
|
|
|
|
We simply set the items old position and update the scene.
|
|
|
|
\snippet tools/undoframework/commands.cpp 3
|
|
|
|
We set the item to its new position.
|
|
|
|
\snippet tools/undoframework/commands.cpp 1
|
|
|
|
Whenever a MoveCommand is created, this function is called to
|
|
check if it should be merged with the previous command. It is the
|
|
previous command object that is kept on the stack. The function
|
|
returns true if the command is merged; otherwise false.
|
|
|
|
We first check whether it is the same item that has been moved
|
|
twice, in which case we merge the commands. We update the position
|
|
of the item so that it will take the last position in the move
|
|
sequence when undone.
|
|
|
|
\section1 DiagramScene Class Definition
|
|
|
|
\snippet tools/undoframework/diagramscene.h 0
|
|
|
|
The DiagramScene implements the functionality to move a
|
|
DiagramItem with the mouse. It emits a signal when a move is
|
|
completed. This is caught by the \c MainWindow, which makes
|
|
MoveCommands. We do not examine the implementation of DiagramScene
|
|
as it only deals with graphics framework issues.
|
|
|
|
\section1 The \c main() Function
|
|
|
|
The \c main() function of the program looks like this:
|
|
|
|
\snippet tools/undoframework/main.cpp 0
|
|
|
|
We draw a grid in the background of the DiagramScene, so we use a
|
|
resource file. The rest of the function creates the \c MainWindow and
|
|
shows it as a top level window.
|
|
*/
|