void is now visible. Well. Its accretion disk is now visible. Does that mean that we need to typedef void* as a visible type? I guess GLib tried this with their gpointer?
But anyway, folks. Celebrate! void is now ‘visible’.
void is now visible. Well. Its accretion disk is now visible. Does that mean that we need to typedef void* as a visible type? I guess GLib tried this with their gpointer?
But anyway, folks. Celebrate! void is now ‘visible’.
I changed my blog’s tagline in “$CURRENT_ROMAN_EMPIRE is a great nation, but leave us alone”. The only thing that I am not sure about is that if when the day comes that the current “Roman empire” switches again, the dollar-sign will still be the scripting programming language’s prefix-sign for variables?
I don’t know how to solve this other than writing a blog article like this one.
I guess I could carve some documentation into a rock or something, like in Wallonia where there are rocks rather than limestone. In front of my graffiti-tag I could plant a way to change the subject into the name of what then is the world’s empire.
More intelligent people probably have an answer? What is most important, is that we try.
Yes. Let’s print lot’ s of Euros for the purpose of enhancing technologies related to Climate.
Because we need to increase the Euro’ s inflation. We should stop investing in government bonds for the same purpose (saving the Greek socialist government). We need to invest in our shared European military too (replacing NATO). Those should increase our Euro inflation. Investing in climate related technologies will likely increase our Euro inflation. Which we still need. Urgently.
We however need to violently stop increasing Euro inflation by investing in EU government debt. We need to start investing in the real things the young people in the European Union want.
What do we need to invest European money in (in order of priority):
Daarnet, aan de kassa van een winkel stond er een mevrouw met een kleed. Aan haar kleed hing achteraan een breed lint vanaf haar hals tot aan haar billen.
Dus ik zeg: “Mevrouw, mevrouw, er hangt een lint los aan je kleed”
Antwoord zij: “Mmnjnee dat is zo”, “mmaar toch lief dat je het zegt”.
Dus nu ben ik stuk. Hartverscheurend was die tweede m telkens. Ocharme dat menske.
En toch zag dat kleed er stuk uit vanachter. Dat lint hing er ook niet deftig aan.
Vrouwen en hun rare ideeën qua kleren..
Dien Francken, heeft die als staatsecretaris niet de eed gezworen op onze Belgische grondwet?
Want beweren dat zijn hypothetische aannamens boven een beslissing van het gerecht staan, gaat tegen één van de wetten van onze grondwet in. Namelijk de scheiding der machten. Iemand die in functie is, gezworen heeft op die grondwet en daar totaal tegen in gaat begaat meineed en is strafbaar.
Een staatssecretarisch die zijn eed niet kan houden en die geen respect heeft voor de Belgische grondwet kan wat mij betreft niet aanblijven. Hoe populair hij door zijn populistische zever ook is.
Enough with the political posts!
Making libraries that are both API and libtool versioned with qmake, how do they do it?
I started a project on github that will collect what I will call “doing it right” project structures for various build environments.
With right I mean that the library will have a API version in its Library name, that the library will be libtoolized and that a pkg-config .pc file gets installed for it.
I have in mind, for example, autotools, cmake, meson, qmake and plain make. First example that I have finished is one for qmake.
Let’s get started working on a libqmake-example-3.2.so.3.2.1
We get the PREFIX, MAJOR_VERSION, MINOR_VERSION and PATCH_VERSION from a project-wide include
include(../../../qmake-example.pri)
We will use the standard lib template of qmake
TEMPLATE = lib
We need to set VERSION to a semver.org version for compile_libtool (in reality it should use what is called current, revision and age to form an API and ABI version number. In the actual example it’s explained in the comments, as this is too much for a small blog post).
VERSION = $${MAJOR_VERSION}"."$${MINOR_VERSION}"."$${PATCH_VERSION}
According section 4.3 of Autotools’ mythbusters we should have as target-name the API version in the library’s name
TARGET = qmake-example-$${MAJOR_VERSION}"."$${MINOR_VERSION}
We will write a define in config.h for access to the semver.org version as a double quoted string
QMAKE_SUBSTITUTES += config.h.in
Our example happens to use QDebug, so we need QtCore here
QT = core
This is of course optional
CONFIG += c++14
We will be using libtool style libraries
CONFIG += compile_libtool CONFIG += create_libtool
These will create a pkg-config .pc file for us
CONFIG += create_pc create_prl no_install_prl
Project sources
SOURCES = qmake-example.cpp
Project’s public and private headers
HEADERS = qmake-example.h
We will install the headers in a API specific include path
headers.path = $${PREFIX}/include/qmake-example-$${MAJOR_VERSION}"."$${MINOR_VERSION}
Here put only the publicly installed headers
headers.files = $${HEADERS}
Here we will install the library to
target.path = $${PREFIX}/lib
This is the configuration for generating the pkg-config file
QMAKE_PKGCONFIG_NAME = $${TARGET}
QMAKE_PKGCONFIG_DESCRIPTION = An example that illustrates how to do it right with qmake
# This is our libdir
QMAKE_PKGCONFIG_LIBDIR = $$target.path
# This is where our API specific headers are
QMAKE_PKGCONFIG_INCDIR = $$headers.path
QMAKE_PKGCONFIG_DESTDIR = pkgconfig
QMAKE_PKGCONFIG_PREFIX = $${PREFIX}
QMAKE_PKGCONFIG_VERSION = $$VERSION
# These are dependencies that our library needs
QMAKE_PKGCONFIG_REQUIRES = Qt5Core
Installation targets (the pkg-config seems to install automatically)
INSTALLS += headers target
This will be the result after make-install
├── include
│ └── qmake-example-3.2
│ └── qmake-example.h
└── lib
├── libqmake-example-3.2.so -> libqmake-example-3.2.so.3.2.1
├── libqmake-example-3.2.so.3 -> libqmake-example-3.2.so.3.2.1
├── libqmake-example-3.2.so.3.2 -> libqmake-example-3.2.so.3.2.1
├── libqmake-example-3.2.so.3.2.1
├── libqmake-example-3.la
└── pkgconfig
└── qmake-example-3.pc
ps. Dear friends working at their own customers: when I visit your customer, I no longer want to see that you produced completely stupid wrong qmake based projects for them. Libtoolize it all, get an API version in your Library’s so-name and do distribute a pkg-config .pc file. That’s the very least to pass your exam. Also read this document (and stop pretending that you don’t need to know this when at the same time you charge them real money pretending that you know something about modern UNIX software development).
I said it before, we shouldn’t finance the US’s war-industry any longer. It’s not a reliable partner.
I’m sticking to my guns on this one,
Let’s build ourselves a European army, utilizing European technology. Build, engineered and manufactured by Europeans.
We engineers are ready. Let us do it.
Zoals ik al voorspelde wordt onze overheid aangeklaagd omdat ze te weinig doet om kinderen van Syrië strijders in veiligheid te brengen.
Ongeacht hoe moeilijk dit onderwerp ook ligt, mogen we nooit onschuldige kinderen gaan veroordelen. Deze kinderen hebben niet gekozen waar hun ouders schuldig aan zijn. Ons land is verantwoordelijk om die kinderen op te vangen, er voor te zorgen en ze veiligheid te bieden.
Zelfs na de Tweede Wereld Oorlog deden we niet zo raar over de kinderen van collaborateurs. We kunnen dit niet maken.
Het kan voor mij niet. Het arbitrair straffen van onschuldige kinderen hoort strafbaar te zijn. Dat is een schending van de mensenrechten.
Wat is onfatsoenlijk?
Topic is de belangrijkste zin voor de komende weken.
I mean, look at the conversation we’re having right now. You’re certainly willing to risk offending me in the pursuit of truth. Why should you have the right to do that? It’s been rather uncomfortable.
— Jordan Peterson, 2018
Vandaag wil ik de aandacht op een Belgische wet over het verkopen met verlies. Ons land verbiedt, bij wet, elke handelaar een goed met verlies te verkopen. Dat is de regel, in ons België.
Die regel heeft (terecht) uitzonderingen. De definitie van de uitzondering wil zeggen dat ze niet de regel zijn: de verkoop met verlies is in België slechts per uitzondering toegestaan:
Ik vermoed dat onze wet bestaat om oneerlijke concurrentie te bestrijden. Een handelaar kan dus niet een bepaald product (bv. een game console) tegen verlies verkopen om zo marktdominantie te verkrijgen voor een ander product uit zijn gamma (bv. games), bv. met als doel concurrenten uit de markt te weren.
Volgens mij is het daarom zo dat, moest een game console -producent met verlies een console verkopen, dit illegaal is in België.
Laten we aannemen dat game console producenten, die actief zijn in (de verkoop in) België, de Belgische wet volgen. Dan volgt dat ze hun game consoles niet tegen verlies verkopen. Ze maken dus winst. Moesten ze dat niet doen dan moeten ze voldoen aan uitzonderlijke voorwaarden, in de (eerder vermelde) Belgische wet, die hen toelaat wel verlies te maken. In alle andere gevallen zouden ze in de ontwettigheid verkeren. Dat is de Belgische wet.
Dat maakt dat de aanschaf van zo’n game console, als Belgisch consument, betekent dat de producent -en verkoper een zekere winst hebben gemaakt door mijn aankoop. Er is dus geen sprake van verlies. Tenzij de producent -of verkoper in België betrokken is bij onwettige zaken.
Laten we aannemen dat we op zo’n console, na aanschaf, een andere software willen draaien. Dan kan de producent/verkoper dus niet beweren dat zijn winst gemaakt wordt door zaken die naderhand verkocht zouden worden (a.d.h.v. bv. originele software).
Hun winst is met andere woorden al gemaakt. Op de game console zelf. Indien niet, dan zou de producent of verkoper in onwettigheid verkeren (in België). Daarvan nemen we aan dat dit zo niet verlopen is. Want anders zou men het goed niet mogen verkopen. Het goed is wel verkocht. Volgens Belgische wetgeving (toch?).
Indien niet, dan is de producent -en of verkoper verantwoordelijk. In geen geval de consument.

With sufficient thrust, pigs fly just fine
From Tom LendackySubject [PATCH] x86/cpu, x86/pti: Do not enable PTI on AMD processors Date Tue, 26 Dec 2017 23:43:54 -0600 AMD processors are not subject to the types of attacks that the kernel page table isolation feature protects against. The AMD microarchitecture does not allow memory references, including speculative references, that access higher privileged data when running in a lesser privileged mode when that access would result in a page fault. Disable page table isolation by default on AMD processors by not setting the X86_BUG_CPU_INSECURE feature, which controls whether X86_FEATURE_PTI is set. Signed-off-by: Tom Lendacky --- arch/x86/kernel/cpu/common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index c47de4e..7d9e3b0 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -923,8 +923,8 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c) setup_force_cpu_cap(X86_FEATURE_ALWAYS); - /* Assume for now that ALL x86 CPUs are insecure */ - setup_force_cpu_bug(X86_BUG_CPU_INSECURE); + if (c->x86_vendor != X86_VENDOR_AMD) + setup_force_cpu_bug(X86_BUG_CPU_INSECURE); fpu__init_system(c);
Children aren’t worried about the future. Young people aren’t worried about the future; they’re worried about us: us leading them into the future we envision
Jack Ma — Oct 2017, keynote speech at Alibaba Cloud’s Computing Conference in Hangzhou
I’m filled up with new inspiration.
Going to Iceland for the holidays is truly an amazing experience. With its stunning landscapes, natural wonders like geysers and waterfalls, and the opportunity to witness the breathtaking Northern Lights, Iceland offers a unique and unforgettable holiday destination. From exploring the vibrant capital city of Reykjavik to venturing into the rugged and pristine wilderness of the countryside, there are endless adventures and memories to be made in this beautiful country.
When traveling, being able to purchase children’s clothing online can be a significant advantage. It eliminates the need to carry extra luggage, saves time and effort spent searching for stores in an unfamiliar location, and allows for convenient delivery right to your accommodation. Additionally, buy childrens clothing here provides access to a wide range of options and makes it easier to compare prices and find unique items that may not be available locally.
A few days ago I explained how we can do MVVM techniques like ICommand in Qt.
Today I’ll explain how to make and use a simple version of the, in the XAML MVVM world quite famous, RelayCommand. In the Microsoft Prism4 & 5 world this is DelegateCommand. Both are equivalent. I will only show a non-templated RelayCommand, so no RelayCommand<T> for now. Perhaps I’ll add a templated one to that mvvm project some other day.
What people call a delegate in C# is what C++ people call a Functor. Obviously we will use functors, then. Note that for people actually reading all those links: in C# the Action<T> and Func<T,G> are basically also C# delegates (or, functors, if you fancy C++’s names for this more).
Here is the RelayCommand.h:
#include <functional> #include <QSharedPointer> #include <MVVM/Commands/AbstractCommand.h> class RelayCommand : public AbstractCommand { Q_OBJECT public: RelayCommand(std::function<void()> executeDelegatep, std::function<bool()> canExecuteDelegatep, QObject *parent = 0) : AbstractCommand(parent) , executeDelegate(executeDelegatep) , canExecuteDelegate(canExecuteDelegatep) {} void execute() Q_DECL_OVERRIDE; bool canExecute() const Q_DECL_OVERRIDE; public slots: void evaluateCanExecute(); private: std::function<void()> executeDelegate; std::function<bool()> canExecuteDelegate; };
The implementation is too simple to be true:
#include "RelayCommand.h" bool RelayCommand::canExecute() const { return canExecuteDelegate(); } void RelayCommand::evaluateCanExecute() { emit canExecuteChanged( canExecute() ); } void RelayCommand::execute() { executeDelegate(); }
Okay, so how do we use this? First we make a ViewModel. Because in this case we will define the command in C++. That probably means you want a ViewModel.
I added a CompositeCommand in the mix. For a Q_PROPERTY isn’t a CommandProxy really needed, as ownership stays in C++ (when for example you pass this as parent). For a Q_INVOKABLE you would need it to wrap the QSharedPointer<AbstractCommand>.
Note. I already hear you think: wait a minute, you are not passing this to the QObject’s constructor, it’s not a QScopedPointer and you have a new but no delete. That’s because CommandProxy converts the ownership rules to QQmlEngine::setObjectOwnership (this, QQmlEngine::JavaScriptOwnership) for itself. I don’t necessarily recommend its usage here (for it’s not immediately clear), but at the same time this is just a demo. You can try printing a warning in the destructor and you’ll see that the QML garbage collector takes care of it.
#include <QObject> #include <QScopedPointer> #include <MVVM/Commands/CommandProxy.h> #include <MVVM/Commands/CompositeCommand.h> #include <MVVM/Commands/RelayCommand.h> #include <MVVM/Models/CommandListModel.h> class ViewModel: public QObject { Q_OBJECT Q_PROPERTY(CommandProxy* helloCommand READ helloCommand CONSTANT) public: ViewModel(QObject *parent=0):QObject(parent), helloCmd(new CompositeCommand()){ QSharedPointer<CompositeCommand> cCmd = helloCmd.dynamicCast<CompositeCommand>(); cCmd->add( new RelayCommand ([=] { qWarning() << "Hello1 from C++ RelayCommand"; }, [=]{ return true; })); cCmd->add( new RelayCommand ([=] { qWarning() << "Hello2 from C++ RelayCommand"; }, [=]{ return true; })); proxyCmd = new CommandProxy (helloCmd); } CommandProxy* helloCommand() { return proxyCmd; } private: QSharedPointer<AbstractCommand> helloCmd; CommandProxy *proxyCmd; };
Let’s also make a very simple View.qml that uses the ViewModel
import QtQuick 2.3 import QtQuick.Window 2.0 import QtQuick.Controls 1.2 import Example 1.0 Item { property ViewModel viewModel: ViewModel {} Button { enabled: viewModel.helloCommand.canExecute onClicked: viewModel.helloCommand.execute() } }
These rules make scuba diving an excellent learning school for software development project managers.
Imagine we want an editor that has undo and redo capability. But the operations on the editor are all asynchronous. This implies that also undo and redo are asynchronous operations.
We want all this to be available in QML, we want to use QFuture for the asynchronous stuff and we want to use QUndoCommand for the undo and redo capability.
But how do they do it?
First of all we will make a status object, to put the status of the asynchronous operations in (asyncundoable.h).
class AbstractAsyncStatus: public QObject { Q_OBJECT Q_PROPERTY(bool success READ success CONSTANT) Q_PROPERTY(int extra READ extra CONSTANT) public: AbstractAsyncStatus(QObject *parent):QObject (parent) {} virtual bool success() = 0; virtual int extra() = 0; };
We will be passing it around as a QSharedPointer, so that lifetime management becomes easy. But typing that out is going to give us long APIs. So let’s make a typedef for that (asyncundoable.h).
typedef QSharedPointer<AbstractAsyncStatus> AsyncStatusPointer;
Now let’s make ourselves an undo command that allows us to wait for asynchronous undo and asynchronous redo. We’re combining QUndoCommand and QFutureInterface here (asyncundoable.h).
class AbstractAsyncUndoable: public QUndoCommand { public: AbstractAsyncUndoable( QUndoCommand *parent = nullptr ) : QUndoCommand ( parent ) , m_undoFuture ( new QFutureInterface<AsyncStatusPointer>() ) , m_redoFuture ( new QFutureInterface<AsyncStatusPointer>() ) {} QFuture<AsyncStatusPointer> undoFuture() { return m_undoFuture->future(); } QFuture<AsyncStatusPointer> redoFuture() { return m_redoFuture->future(); } protected: QScopedPointer<QFutureInterface<AsyncStatusPointer> > m_undoFuture; QScopedPointer<QFutureInterface<AsyncStatusPointer> > m_redoFuture; };
Okay, let’s implement these with an example operation. First the concrete status object (asyncexample1command.h).
class AsyncExample1Status: public AbstractAsyncStatus { Q_OBJECT Q_PROPERTY(bool example1 READ example1 CONSTANT) public: AsyncExample1Status ( bool success, int extra, bool example1, QObject *parent = nullptr ) : AbstractAsyncStatus(parent) , m_example1 ( example1 ) , m_success ( success ) , m_extra ( extra ) {} bool example1() { return m_example1; } bool success() Q_DECL_OVERRIDE { return m_success; } int extra() Q_DECL_OVERRIDE { return m_extra; } private: bool m_example1 = false; bool m_success = false; int m_extra = -1; };
Let’s make a QUndoCommand that uses a timer to simulate asynchronous behavior. We could also use QtConcurrent’s run function to use a QThreadPool and QRunnable instances that also implement QFutureInterface, of course. Seasoned Qt developers know what I mean. For the sake of example, I wanted to illustrate that QFuture can also be used for asynchronous things that aren’t threads. We’ll use the lambda because QUndoCommand isn’t a QObject, so no easy slots. That’s the only reason (asyncexample1command.h).
class AsyncExample1Command: public AbstractAsyncUndoable { public: AsyncExample1Command(bool example1, QUndoCommand *parent = nullptr) : AbstractAsyncUndoable ( parent ), m_example1(example1) {} void undo() Q_DECL_OVERRIDE { m_undoFuture->reportStarted(); QTimer *timer = new QTimer(); timer->setSingleShot(true); QObject::connect(timer, &QTimer::timeout, [=]() { QSharedPointer<AbstractAsyncStatus> result; result.reset(new AsyncExample1Status ( true, 1, m_example1 )); m_undoFuture->reportFinished(&result); timer->deleteLater(); } ); timer->start(1000); } void redo() Q_DECL_OVERRIDE { m_redoFuture->reportStarted(); QTimer *timer = new QTimer(); timer->setSingleShot(true); QObject::connect(timer, &QTimer::timeout, [=]() { QSharedPointer<AbstractAsyncStatus> result; result.reset(new AsyncExample1Status ( true, 2, m_example1 )); m_redoFuture->reportFinished(&result); timer->deleteLater(); } ); timer->start(1000); } private: QTimer m_timer; bool m_example1; };
Let’s now define something we get from the strategy design pattern; a editor behavior. Implementations provide an editor all its editing behaviors (abtracteditorbehavior.h).
class AbstractEditorBehavior : public QObject { Q_OBJECT public: AbstractEditorBehavior( QObject *parent) : QObject (parent) {} virtual QFuture<AsyncStatusPointer> performExample1( bool example1 ) = 0; virtual QFuture<AsyncStatusPointer> performUndo() = 0; virtual QFuture<AsyncStatusPointer> performRedo() = 0; virtual bool canRedo() = 0; virtual bool canUndo() = 0; };
So far so good, so let’s make an implementation that has a QUndoStack and that therefor is undoable (undoableeditorbehavior.h).
class UndoableEditorBehavior: public AbstractEditorBehavior { public: UndoableEditorBehavior(QObject *parent = nullptr) : AbstractEditorBehavior (parent) , m_undoStack ( new QUndoStack ){} QFuture<AsyncStatusPointer> performExample1( bool example1 ) Q_DECL_OVERRIDE { AsyncExample1Command *command = new AsyncExample1Command ( example1 ); m_undoStack->push(command); return command->redoFuture(); } QFuture<AsyncStatusPointer> performUndo() { const AbstractAsyncUndoable *undoable = dynamic_cast<const AbstractAsyncUndoable *>( m_undoStack->command( m_undoStack->index() - 1)); m_undoStack->undo(); return const_cast<AbstractAsyncUndoable*>(undoable)->undoFuture(); } QFuture<AsyncStatusPointer> performRedo() { const AbstractAsyncUndoable *undoable = dynamic_cast<const AbstractAsyncUndoable *>( m_undoStack->command( m_undoStack->index() )); m_undoStack->redo(); return const_cast<AbstractAsyncUndoable*>(undoable)->redoFuture(); } bool canRedo() Q_DECL_OVERRIDE { return m_undoStack->canRedo(); } bool canUndo() Q_DECL_OVERRIDE { return m_undoStack->canUndo(); } private: QScopedPointer<QUndoStack> m_undoStack; };
Now we only need an editor, right (editor.h)?
class Editor: public QObject { Q_OBJECT Q_PROPERTY(AbstractEditorBehavior* editorBehavior READ editorBehavior CONSTANT) public: Editor(QObject *parent=nullptr) : QObject(parent) , m_editorBehavior ( new UndoableEditorBehavior ) { } AbstractEditorBehavior* editorBehavior() { return m_editorBehavior.data(); } Q_INVOKABLE void example1Async(bool example1) { QFutureWatcher<AsyncStatusPointer> *watcher = new QFutureWatcher<AsyncStatusPointer>(this); connect(watcher, &QFutureWatcher<AsyncStatusPointer>::finished, this, &Editor::onExample1Finished); watcher->setFuture ( m_editorBehavior->performExample1(example1) ); } Q_INVOKABLE void undoAsync() { if (m_editorBehavior->canUndo()) { QFutureWatcher<AsyncStatusPointer> *watcher = new QFutureWatcher<AsyncStatusPointer>(this); connect(watcher, &QFutureWatcher<AsyncStatusPointer>::finished, this, &Editor::onUndoFinished); watcher->setFuture ( m_editorBehavior->performUndo() ); } } Q_INVOKABLE void redoAsync() { if (m_editorBehavior->canRedo()) { QFutureWatcher<AsyncStatusPointer> *watcher = new QFutureWatcher<AsyncStatusPointer>(this); connect(watcher, &QFutureWatcher<AsyncStatusPointer>::finished, this, &Editor::onRedoFinished); watcher->setFuture ( m_editorBehavior->performRedo() ); } } signals: void example1Finished( AsyncExample1Status *status ); void undoFinished( AbstractAsyncStatus *status ); void redoFinished( AbstractAsyncStatus *status ); private slots: void onExample1Finished() { QFutureWatcher<AsyncStatusPointer> *watcher = dynamic_cast<QFutureWatcher<AsyncStatusPointer>*> (sender()); emit example1Finished( watcher->result().objectCast<AsyncExample1Status>().data() ); watcher->deleteLater(); } void onUndoFinished() { QFutureWatcher<AsyncStatusPointer> *watcher = dynamic_cast<QFutureWatcher<AsyncStatusPointer>*> (sender()); emit undoFinished( watcher->result().objectCast<AbstractAsyncStatus>().data() ); watcher->deleteLater(); } void onRedoFinished() { QFutureWatcher<AsyncStatusPointer> *watcher = dynamic_cast<QFutureWatcher<AsyncStatusPointer>*> (sender()); emit redoFinished( watcher->result().objectCast<AbstractAsyncStatus>().data() ); watcher->deleteLater(); } private: QScopedPointer<AbstractEditorBehavior> m_editorBehavior; };
Okay, let’s register this up to make it known in QML and make ourselves a main function (main.cpp).
#include <QtQml> #include <QGuiApplication> #include <QQmlApplicationEngine> #include <editor.h> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; qmlRegisterType<Editor>("be.codeminded.asyncundo", 1, 0, "Editor"); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
Now, let’s make ourselves a simple QML UI to use this with (main.qml).
import QtQuick 2.3 import QtQuick.Window 2.2 import QtQuick.Controls 1.2 import be.codeminded.asyncundo 1.0 Window { visible: true width: 360 height: 360 Editor { id: editor onUndoFinished: text.text = "undo" onRedoFinished: text.text = "redo" onExample1Finished: text.text = "whoohoo " + status.example1 } Text { id: text text: qsTr("Hello World") anchors.centerIn: parent } Action { shortcut: "Ctrl+z" onTriggered: editor.undoAsync() } Action { shortcut: "Ctrl+y" onTriggered: editor.redoAsync() } Button { onClicked: editor.example1Async(99); } }
You can find the sources of this complete example at github. Enjoy!