QskModelObjectBinder API improved, models example polished

This commit is contained in:
Uwe Rathmann 2024-02-26 15:27:52 +01:00
parent 7b4db3afc1
commit 0a651782ba
7 changed files with 259 additions and 137 deletions

View File

@ -3,5 +3,5 @@
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
############################################################################ ############################################################################
qsk_add_example(models Window.h Window.cpp main.cpp) qsk_add_example(models MainView.h MainView.cpp main.cpp)
target_link_libraries(models) target_link_libraries(models)

View File

@ -0,0 +1,215 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "MainView.h"
#include <QskLinearBox.h>
#include <QskPushButton.h>
#include <QskTextLabel.h>
#include <QskTextInput.h>
#include <QskSpinBox.h>
#include <QskSeparator.h>
#include <QskFontRole.h>
#include <QskModelObjectBinder.h>
#include <QStandardItemModel>
namespace
{
class TitleLabel : public QskTextLabel
{
public:
TitleLabel( const QString& text, QQuickItem* parent = nullptr )
: QskTextLabel( text, parent )
{
setFontRole( QskFontRole::Title );
}
};
class SpinBox : public QskSpinBox
{
public:
SpinBox( QQuickItem* parent = nullptr )
: QskSpinBox( -100.0, 100.0, 1.0, parent )
{
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
}
};
class Header : public QskLinearBox
{
Q_OBJECT
public:
Header( QQuickItem* parent = nullptr )
: QskLinearBox( Qt::Horizontal, parent )
{
initSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::Fixed );
setPaddingHint( QskBox::Panel, 5 );
setPanel( true );
auto rowButton = new QskPushButton( "Toggle Row" );
auto submitButton = new QskPushButton( "Submit Changes" );
connect( rowButton, &QskPushButton::clicked,
this, &Header::rowClicked );
connect( submitButton, &QskPushButton::clicked,
this, &Header::submitClicked );
addItem( rowButton );
addItem( submitButton );
addStretch( 1 );
}
Q_SIGNALS:
void rowClicked();
void submitClicked();
};
class Model : public QStandardItemModel
{
public:
Model( QObject* parent = nullptr )
: QStandardItemModel( 2, 2, parent )
{
initValue( 0, 0, 1.0 );
initValue( 0, 1, "HELLO" );
initValue( 1, 0, 2.0 );
initValue( 1, 1, "WORLD" );
}
private:
void initValue( int row, int col, const QVariant& value )
{
setData( index( row, col ), value, Qt::EditRole );
}
};
class DisplayBox : public QskLinearBox
{
public:
DisplayBox( QQuickItem* parent = nullptr )
: QskLinearBox( Qt::Horizontal, parent )
{
setMargins( 10, 0, 10, 0 );
initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Fixed );
addItem( new SpinBox() );
addItem( new QskTextInput() );
}
};
class ModelBox : public QskLinearBox
{
public:
ModelBox( QAbstractItemModel* model )
: QskLinearBox( Qt::Horizontal, model->columnCount() )
, m_model( model )
{
setMargins( 10, 0, 10, 0 );
setExtraSpacingAt( Qt::BottomEdge );
for ( int row = 0; row < model->rowCount(); row++ )
{
for ( int col = 0; col < model->columnCount(); col++ )
{
const auto value = model->data(
model->index( row, col ), Qt::EditRole );
if ( value.userType() == QVariant::Double )
{
auto spinBox = new SpinBox( this );
connect( spinBox, &QskSpinBox::valueChanged,
this, &ModelBox::updateModel );
}
else
{
auto textInput = new QskTextInput( this );
connect( textInput, &QskTextInput::textChanged,
this, &ModelBox::updateModel );
}
}
}
updateDisplay();
connect( m_model, &Model::dataChanged, this, &ModelBox::updateDisplay );
}
private:
void updateModel()
{
if ( auto item = qobject_cast< const QQuickItem* >( sender() ) )
{
const int index = indexOf( item );
const auto modelIndex = m_model->index(
index / dimension(), index % dimension() );
const auto property = item->metaObject()->userProperty();
m_model->setData( modelIndex, property.read( item ), Qt::EditRole );
}
}
void updateDisplay() const
{
for ( int row = 0; row < m_model->rowCount(); row++ )
{
for ( int col = 0; col < m_model->columnCount(); col++ )
{
const auto index = m_model->index( row, col );
if ( auto item = itemAtIndex( row * dimension() + col ) )
{
const auto property = item->metaObject()->userProperty();
property.write( item, m_model->data( index, Qt::EditRole ) );
}
}
}
}
QPointer< QAbstractItemModel > m_model;
};
}
MainView::MainView( QQuickItem* parent )
: QskMainView( parent )
{
m_binder = new QskModelObjectBinder( new Model( this ), this );
auto header = new Header();
auto displayBox = new DisplayBox();
for ( int i = 0; i < displayBox->elementCount(); i++ )
m_binder->bindObject( displayBox->itemAtIndex( i ), i );
auto box = new QskLinearBox( Qt::Vertical );
box->addItem( new TitleLabel( "Editor:" ) );
box->addItem( displayBox );
box->addItem( new QskSeparator() );
box->addItem( new TitleLabel( "Model:" ) );
box->addItem( new ModelBox( m_binder->model() ) );
setHeader( header );
setBody( box );
connect( header, &Header::rowClicked,
this, &MainView::toogleRow );
connect( header, &Header::submitClicked,
m_binder, &QskModelObjectBinder::submit );
}
void MainView::toogleRow()
{
m_binder->setCurrentRow( m_binder->currentRow() == 0 ? 1 : 0 );
}
#include "MainView.moc"

View File

@ -5,10 +5,17 @@
#pragma once #pragma once
#include <QskWindow.h> #include <QskMainView.h>
class Window : public QskWindow class QskModelObjectBinder;
class MainView : public QskMainView
{ {
public: public:
Window(); MainView( QQuickItem* = nullptr );
private:
void toogleRow();
QskModelObjectBinder* m_binder;
}; };

View File

@ -1,128 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "Window.h"
#include <QskLinearBox.h>
#include <QskPushButton.h>
#include <QskTextInput.h>
#include <QskSpinBox.h>
#include <QskModelObjectBinder.h>
#include <QStandardItemModel>
namespace
{
class Model : public QStandardItemModel
{
public:
Model( QObject* parent = nullptr )
: QStandardItemModel( 2, 2, parent )
{
initValue( 0, 0, 1 );
initValue( 0, 1, "HELLO" );
initValue( 1, 0, 2 );
initValue( 1, 1, "WORLD" );
}
void dump() const
{
qDebug() << "Model";
for ( int row = 0; row < rowCount(); row++ )
{
for ( int col = 0; col < columnCount(); col++ )
{
qDebug() << '\t' << row << col
<< data( index( row, col ), Qt::EditRole );
}
}
}
private:
void initValue( int row, int col, const QVariant& value )
{
setData( index( row, col ), value, Qt::EditRole );
}
};
class View : public QskLinearBox
{
public:
View( QQuickItem* parent = nullptr )
: QskLinearBox( Qt::Vertical, parent )
{
setPanel( true );
auto model = new Model( this );
auto textInput = new QskTextInput();
auto spinBox = new QskSpinBox( -100.0, +100.0, 1.0 );
m_binder = new QskModelObjectBinder( model, this );
m_binder->bindObject( spinBox, 0 );
m_binder->bindObject( textInput, 1 );
auto hBox = new QskLinearBox( Qt::Horizontal );
hBox->setSection( QskAspect::Header );
hBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed );
{
auto rowButton = new QskPushButton( "Toggle Row", hBox );
auto counterButton = new QskPushButton( "Invert Counter", hBox );
auto submitButton = new QskPushButton( "Submit Changes", hBox );
connect( rowButton, &QskPushButton::clicked,
this, &View::toogleRow );
connect( counterButton, &QskPushButton::clicked,
this, &View::invertCounter );
connect( submitButton, &QskPushButton::clicked,
this, &View::updateModel );
hBox->addStretch( 1 );
}
addItem( hBox );
addSpacer( 5 );
addItem( spinBox );
addItem( textInput );
addStretch( 1 );
}
private:
void toogleRow()
{
m_binder->setCurrentRow( m_binder->currentRow() == 0 ? 1 : 0 );
}
void updateModel()
{
m_binder->submit();
const auto model = dynamic_cast< const Model* >( m_binder->model() );
model->dump();
}
void invertCounter()
{
auto model = m_binder->model();
const auto index = model->index( m_binder->currentRow(), 0 );
auto value = model->data( index, Qt::EditRole );
model->setData( index, -value.toDouble(), Qt::EditRole );
}
QskModelObjectBinder* m_binder;
};
}
Window::Window()
{
addItem( new View() );
}

View File

@ -3,12 +3,13 @@
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/ *****************************************************************************/
#include "Window.h" #include "MainView.h"
#include <SkinnyShortcut.h> #include <SkinnyShortcut.h>
#include <QskFocusIndicator.h>
#include <QskObjectCounter.h> #include <QskObjectCounter.h>
#include <QskFocusIndicator.h>
#include <QskWindow.h>
#include <QGuiApplication> #include <QGuiApplication>
@ -22,13 +23,14 @@ int main( int argc, char* argv[] )
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
Window window; QskWindow window;
window.resize( 600, 400 ); window.resize( 600, 400 );
auto focusIndicator = new QskFocusIndicator(); auto focusIndicator = new QskFocusIndicator();
focusIndicator->setObjectName( "FocusIndicator" ); focusIndicator->setObjectName( "FocusIndicator" );
window.addItem( focusIndicator ); window.addItem( focusIndicator );
window.addItem( new MainView() );
window.show(); window.show();
return app.exec(); return app.exec();

View File

@ -133,8 +133,8 @@ void QskModelObjectBinder::bindObject(
void QskModelObjectBinder::unbindObject( QObject* object ) void QskModelObjectBinder::unbindObject( QObject* object )
{ {
auto it = m_data->bindings.find( object ); auto it = m_data->bindings.constFind( object );
if ( it != m_data->bindings.end() ) if ( it != m_data->bindings.constEnd() )
{ {
qskEnableConnections( object, this, false ); qskEnableConnections( object, this, false );
m_data->bindings.erase( it ); m_data->bindings.erase( it );
@ -151,6 +151,28 @@ void QskModelObjectBinder::clearBindings()
bindings.clear(); bindings.clear();
} }
QObjectList QskModelObjectBinder::boundObjects() const
{
auto& bindings = m_data->bindings;
QObjectList objects;
objects.reserve( bindings.count() );
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
objects += it.key();
return objects;
}
QMetaProperty QskModelObjectBinder::boundProperty( const QObject* object ) const
{
auto it = m_data->bindings.constFind( const_cast< QObject* >( object ) );
if ( it != m_data->bindings.constEnd() )
return it.value().property;
return QMetaProperty();
}
void QskModelObjectBinder::setModel( QAbstractItemModel* model ) void QskModelObjectBinder::setModel( QAbstractItemModel* model )
{ {
if ( model == m_data->model ) if ( model == m_data->model )

View File

@ -14,6 +14,7 @@
#include <memory> #include <memory>
class QAbstractItemModel; class QAbstractItemModel;
class QMetaProperty;
class QSK_EXPORT QskModelObjectBinder : public QObject class QSK_EXPORT QskModelObjectBinder : public QObject
{ {
@ -39,6 +40,9 @@ class QSK_EXPORT QskModelObjectBinder : public QObject
void unbindObject( QObject* ); void unbindObject( QObject* );
QMetaProperty boundProperty( const QObject* ) const;
QObjectList boundObjects() const;
Q_SIGNALS: Q_SIGNALS:
void currentRowChanged( int ); void currentRowChanged( int );