GUI¶
Important
If you need to quickly find out where a graphical item is defined in the source code, open the application and activate
Then place your mouse over a widget. This will display in the window's status bar where the widget's source code is. This is probably the quickest way to get started.
Directory structure¶
All code related to the GUI resides in src/ui/. The directory structure
(including the most important header files) is as follows:
src/ui/
├── objects/ # GObjects and subclasses
├── widgets/ # Reusable QWidgets
├── windows/ # Windows and dialogs
├── mainwindow.h # MainWindow
├── sheetview.h # SheetView
├── sheetscene.h # SheetScene
├── operations.h # Scene operations
├── commands.h # Undo-able commands
└── vim.h # Vim mode
The main window¶
The main window is implemented as a singleton in the class MainWindow. The
active instance can be obtained using MainWindow::getInstance.
-
class
MainWindow: public QMainWindow¶
-
class
MainWindow: public QMainWindow The main application window.
Note
This is a singleton class.
Public Functions
-
explicit
MainWindow(QWidget *parent = nullptr)
-
~MainWindow()
-
QTabWidget *
getTabWidget() const Return the tab widget
-
int
getTabId() const Return the current tab id
-
SheetView *
getTab(int index = -1) Return the tab of the specified index as a
SheetView*. If no index is specified, return the current tab.
-
SheetScene *
scene()
-
Sheet *
getSheet(int index = -1) Return the
Sheet*that is part of the currently active tab If no index is specified, return the current sheet.
-
bool
isOpen(Sheet *sheet)
-
void
setTabId(int id)
-
void
setVimStatus(const QString &status)
-
void
setActiveProject(Project *project)
-
void
newProject()
-
void
open()
-
void
save()
-
void
saveAs()
-
void
projectSettings()
-
void
preferences()
-
void
print()
-
void
help()
-
void
takeScreenshot()
-
void
toggleDeveloperHints()
-
void
openProjectsFromFiles(const QStringList &filenames, int active = -1)
-
int
openSheet(Sheet *sheet, int index = -1)
-
void
closeSheet(Sheet *sheet)
-
void
closeProject(Project *project)
-
void
closeSheetsForProject(Project *project)
Public Static Functions
-
static MainWindow *
getInstance()
-
explicit
The schematic editor¶
Qt provides QGraphicsView and QGraphicsScene to visually represent QGraphicsItem-s. You should have at least a basic knowledge of these classes if you wish to develop the GUI. We have created custom subclasses of these classes. A sheet is represented by a SheetView and SheetScene, and an object is represented by a GObject.
Note
The base classes are also used on their own in some places.
SheetView¶
Defined in src/ui/sheetview.h
This is a widget that displays a schematic to the user. It cooperates with SheetScene to display and interact with graphical objects.
Rubber band selection¶
Schim supports two types of rubber band selection. If the user initially drags the rubber band to the right, objects are selected only if they are wholly contained in the band. On the other hand, if the user initially drags the band to the left, any objects that are touched by the band are selected. Here's a coarse overview of what happens in the program.
mousePressEventreceives a left click and memorizes the positionSubsequent calls of
mouseMoveEventtry to determine whether dragging is to the left or to the right by callingprocessRubberBandDrag.mouseReleaseEventdisables the state of dragging.
SheetScene¶
Defined in src/ui/sheetscene.h
Scene operations¶
Defined in src/ui/operations.h
Many user actions are composed of multiple steps. For example inserting a line
requires the insertion of two points in succession. For this functionality, we
have created the class SceneOperation.
To start an operation, construct it and call SheetScene::startOperation. The
operation will receive and process any applicable events. Once the operation
finishes, it will emit the SceneOperation::finished signal. The scene will
pick up on this signal.
-
class
SceneOperation: public QObject¶ Subclassed by OpInsertComponent, OpInsertLine, OpInsertRect, OpInsertText
Public Functions
-
explicit
SceneOperation(SheetScene *scene)¶
-
virtual void
mousePressEvent(QGraphicsSceneMouseEvent *event)¶
-
virtual void
mouseMoveEvent(QGraphicsSceneMouseEvent *event)¶
-
virtual void
mouseReleaseEvent(QGraphicsSceneMouseEvent *event)¶
-
virtual void
keyPressEvent(QKeyEvent *event)¶
-
virtual void
cancel()¶
Signals
-
void
finished()¶
-
explicit
-
class
SceneOperation: public QObject The base class of all scene operations.
An operation is any multi-step process that relies on mouse/keyboard input. An example of an operation is inserting a line.
Each operation is associated with a scene.
Subclassed by OpInsertComponent, OpInsertLine, OpInsertRect, OpInsertText
Public Functions
-
explicit
SceneOperation(SheetScene *scene) Construct a scene operation in the specified scene.
-
virtual void
mousePressEvent(QGraphicsSceneMouseEvent *event) Process a mousePressEvent. The base implementation does nothing.
-
virtual void
mouseMoveEvent(QGraphicsSceneMouseEvent *event) Process a mouseMoveEvent. The base implementation does nothing.
-
virtual void
mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Process a mouseReleaseEvent. The base implementation does nothing.
-
virtual void
keyPressEvent(QKeyEvent *event) Process a keyPressEvent. The base implementation does nothing.
-
virtual void
cancel() Cancel the operation.
This will delete any dangling objects and remove them from the scene. The
finishedsignal will be emitted.
Signals
-
void
finished() Emitted when the operation has finished, either successfully or not.
-
explicit
The following operations are currently defined:
Snap feature¶
The snap-to-grid feature is enabled/disabled by calling
SheetScene::setSnapEnabled.
Scene vs view¶
Sometimes it may seem arbitrary whether to implement a feature in the view or in the scene. Sometimes it is. But as a rule of thumb:
Implement a feature in the scene if:
it changes the structure of the sheet
objects need to be aware of the feature and/or react to it
Implement a feature in the view if:
it doesn't change the structure of the sheet (e.g. zooming, panning)
it requires access to the global coordinate system (an exception would be if the global position can be obtained from a mouse event)
it needs to display items that are transform-invariant
multiple views containing the same scene can use the feature at the same time
The general idea is to let the scene know as little as possible about the view. This is because multiple views can be used to display the same scene at the same time. If the scene (or an object by calling a scene function) needs to communicate with a view, use signals and slots. This way, only interested views can pick up on that.
GObject¶
Inheritance graph
Collaboration graph
An object is visually represented as a GObject, which is a wrapper around an Object. Derived classes follow the same naming convention. Namely, the graphical class is named by prepending the corresponding model class name with the letter 'G' (for graphical). For example, a Line is wrapped by GLine.
To construct a GObject to wrap an Object obj call
GObject::assign(obj).
The following classes are derived from GObject:
-
class
GObject: public QGraphicsObject¶ Subclassed by GCompositeObject, GLine, GRect, GTerminal, GText
Public Functions
-
virtual
~GObject()¶
-
bool
isHovered() const¶
-
virtual void
setCosmetic(bool cosmetic)¶
-
virtual void
reloadFromModel()¶
-
virtual void
applyToModel()¶
-
virtual void
showHandles(bool show = true)¶
-
virtual void
handleChanged(GObjectHandle *handle)¶
-
void
hoverEnterEvent(QGraphicsSceneHoverEvent *event) override¶
-
void
hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override¶
-
QRectF
boundingRect() const override¶
-
void
paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override¶
-
GCompositeObject *
parentItem() const¶
-
virtual
-
class
GObject: public QGraphicsObject The base class of all objects represented in a graphical scene.
This class is conceptually an abstract one - it shouldn't be used on its own.
Subclassed by GCompositeObject, GLine, GRect, GTerminal, GText
Public Functions
-
explicit
GObject(Object *obj) Construct a wrapper around
obj.The object is movable, selectable and sends geometry changes by default. Hover events are accepted.
-
virtual
~GObject() Destroy any handles that are still active.
-
virtual Object *
get() Return the object that is being wrapped by this class.
Note
Derived classes should change the return type of this method to match the type of the object that is wrapped by them.
-
virtual const Object *
get() const
-
Entity *
getModelParent() const
-
bool
isHovered() const Return whether the mouse is over this object.
-
GObject *
getOldestParent()
-
SheetScene *
getSheetScene() const
-
virtual void
setCosmetic(bool cosmetic) Make the object's pen independent of any transformations.
Make an object cosmetic when it is going to be rendered to an icon.
-
virtual void
reloadFromModel() Update the graphical representation to match the object from the model.
Note
The base implementation does nothing and should be implemented in derived classes.
-
virtual void
applyToModel() Apply changes to the underlying model object.
Note
The base implementation does nothing and should be implemented in derived classes.
-
virtual void
showHandles(bool show = true) Display/hide the handles for this item.
The list of handles is dynamically allocated and it exists only while the handles are visible. This way, the handles do not take up memory when they are not used.
Note
The base version of this method implements only the deletion of handles, i.e. when
show = true. Each subclass should implement this this method separately, optionally calling the superclass method but only as the last statement.
-
virtual void
handleChanged(GObjectHandle *handle) Called when a handle's position has changed.
The base implementation does nothing.
- Parameters
handle -- The handle that has been modified.
-
void
hoverEnterEvent(QGraphicsSceneHoverEvent *event) override Set
hovered=trueso that it can be used by paint.
-
void
hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override Disable the bool
hoveredso that it can be used by paint.
-
QRectF
boundingRect() const override Default implementation that returns childrenBoundingRect().
-
void
paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) override This implementation applies aesthetic modifications when the object is hovered or selected.
Note
Derived classes should call this version at the beginning of their implementation to apply the aesthetic changes.
-
GCompositeObject *
parentItem() const Return the parent item cast to a
GObject*.
-
explicit
Dialogs¶
Vim mode¶
You should read TODO in the manual before proceeding.
Vim mode is implemented in src/ui/vim.h and src/ui/vim.cpp. It is enabled
and disabled by calling Vim::enable.
When a QWidget or GObject receives a keyPressEvent, it has the
option to process that event in Vim mode. It is supposed to call
Vim::registerKeyPress, providing the incoming event and a pointer to a
callback function as parameters. The callback function will be invoked if a
proper key sequence is matched.
Todo
Add more details.
-
namespace
Vim¶ -
Functions
-
void
enable(bool enable)¶
-
bool
enabled()¶
-
void
addBinding(const QString &sequence, const QString &action)¶
-
void
addBindings(const QMap<QString, QString> &map)¶
-
void
registerKeyPress(QKeyEvent *event, std::function<bool(const Action &action)> callback, bool allowCount = true, )¶
-
void
resetStroke()¶
-
QString
getStatusText()¶
-
class
Action¶
-
class
Count¶
-
void
-
namespace
Vim Everything related to vim-mode inside schim.
Typedefs
-
typedef Count
N Convenience typedef.
Functions
-
void
enable(bool enable) Enable/Disable vim-mode throughout the application instance.
-
bool
enabled() Is vim-mode enabled?
-
Count
n() The vim-count from the current key sequence.
-
void
addBinding(const QString &sequence, const QString &action) Register a keybinding with Vim-mode.
- Parameters
sequence -- The key sequence that triggers
action.action -- The vim-action that is performed when the key sequence is input.
-
void
addBindings(const QMap<QString, QString> &map) Convenient alternative to multiple calls of
Vim::addBinding.- Parameters
map -- Map of bindings to be registered with vim-mode.
-
void
registerKeyPress(QKeyEvent *event, std::function<bool(const Action &action)> callback, bool allowCount = true, ) Process a key press in vim-mode.
When a widget receives a KeyPress event, it should call this function if vim-like behavior is desired. The event will be processed, the current vim key sequence will be updated and it will be shown in the vim status indicator of the main window. If the sequence matches a binding callback will be called with an appropriate
Vim::Action.- See
- See
First we determine if we are listening for the user to input a count (only if
allowCountistrue). We correspondingly process all digits the user enters and form a count.Then, we test if the sequence matches a binding. If it does,
callbackis called. Otherwise, we test if any binding starts with the sequence that has been input so far. If even this is not the case, the sequence is discarded and the vim status is reset.Note
If vim-mode is not enabled, this function does nothing.
Note
If
allowCountisfalse, treat numbers as normal key sequences.- Parameters
event -- KeyPress event that triggered this.
callback -- Callback for when the input key sequence matches a binding.
allowCount -- Process vim-counts as part of a key sequence.
-
void
resetStroke() Discard the current key sequence and reset vim status indicator.
-
QString
getStatusText() Get the vim status text for the current key sequence.
-
class
Action - #include <vim.h>
An action in vim-mode.
A vim-action is a key sequence that is accompanied with some extra data:
a vim-count [optional]
input register [TODO]
a vim-command
a vim-motion [TODO]
output register [TODO]
Public Functions
-
explicit
Action(const QString &command, int count = 0) Construct a vim-action.
-
QString
getCommand() const Get the vim-command associated with this action.
-
Count
getCount() const Get the vim-count associated with this action.
-
bool
operator==(const QString &cmd) const Is this action's command identical to
cmd?
-
class
Count - #include <vim.h>
Representation of a vim-count.
When the user inputs a key sequence, usually a count can also be specified. This count determines how many times the command will be repeated. An unspecified count is equal to
Count(0).Different commands will treat a 0-count differently. Some commands equate a 0-count with a 1-count, others treat it in some special way. This class provides a uniform interface for each use case.
Casting a
Vim::Countto anintyields a basic count, i.e.int(Count(0)) == 1. On the other hand,raw()yields the raw count, i.e.Count(0).raw() == 0.Note
For non-zero counts,
int()andraw()are equivalent.- See
Public Functions
-
Count(int count) Construct a vim-count.
- Parameters
count -- The raw vim-count.
-
operator int() const Conversion to integer.
- Returns
The basic count.
-
int
raw() const Get the raw count from this
Count.
-
typedef Count