Compare commits
17 Commits
master
...
features/m
Author | SHA1 | Date |
---|---|---|
|
9040e006ad | |
|
d9ef4f51ae | |
|
04e3378abb | |
|
52686242ec | |
|
c6fc72e07c | |
|
69eae90d3a | |
|
a888d9c5a8 | |
|
cd8fd0d2b2 | |
|
6a547e4698 | |
|
0a651782ba | |
|
7b4db3afc1 | |
|
e2486f914d | |
|
60c7442fd0 | |
|
8faa6a8cad | |
|
1a5dc6d358 | |
|
59afa42c84 | |
|
6ea2126638 |
|
@ -9,6 +9,7 @@ add_subdirectory(shadows)
|
|||
add_subdirectory(shapes)
|
||||
add_subdirectory(charts)
|
||||
add_subdirectory(plots)
|
||||
add_subdirectory(models)
|
||||
|
||||
if (BUILD_INPUTCONTEXT)
|
||||
add_subdirectory(inputpanel)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
############################################################################
|
||||
# QSkinny - Copyright (C) The authors
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
############################################################################
|
||||
|
||||
qsk_add_example(models MainView.h MainView.cpp main.cpp)
|
||||
target_link_libraries(models)
|
|
@ -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 <QskTextField.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 QskTextField() );
|
||||
}
|
||||
};
|
||||
|
||||
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 textField = new QskTextField( this );
|
||||
connect( textField, &QskTextField::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"
|
|
@ -0,0 +1,21 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QskMainView.h>
|
||||
|
||||
class QskModelObjectBinder;
|
||||
|
||||
class MainView : public QskMainView
|
||||
{
|
||||
public:
|
||||
MainView( QQuickItem* = nullptr );
|
||||
|
||||
private:
|
||||
void toogleRow();
|
||||
|
||||
QskModelObjectBinder* m_binder;
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "MainView.h"
|
||||
|
||||
#include <SkinnyShortcut.h>
|
||||
|
||||
#include <QskObjectCounter.h>
|
||||
#include <QskFocusIndicator.h>
|
||||
#include <QskWindow.h>
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
#ifdef ITEM_STATISTICS
|
||||
QskObjectCounter counter( true );
|
||||
#endif
|
||||
|
||||
QGuiApplication app( argc, argv );
|
||||
|
||||
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
|
||||
|
||||
QskWindow window;
|
||||
window.resize( 600, 400 );
|
||||
|
||||
auto focusIndicator = new QskFocusIndicator();
|
||||
focusIndicator->setObjectName( "FocusIndicator" );
|
||||
|
||||
window.addItem( focusIndicator );
|
||||
window.addItem( new MainView() );
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
|
@ -233,6 +233,7 @@ list(APPEND HEADERS
|
|||
controls/QskListViewSkinlet.h
|
||||
controls/QskMenu.h
|
||||
controls/QskMenuSkinlet.h
|
||||
controls/QskModelObjectBinder.h
|
||||
controls/QskObjectTree.h
|
||||
controls/QskPageIndicator.h
|
||||
controls/QskPageIndicatorSkinlet.h
|
||||
|
@ -345,6 +346,7 @@ list(APPEND SOURCES
|
|||
controls/QskListViewSkinlet.cpp
|
||||
controls/QskMenuSkinlet.cpp
|
||||
controls/QskMenu.cpp
|
||||
controls/QskModelObjectBinder.cpp
|
||||
controls/QskObjectTree.cpp
|
||||
controls/QskPageIndicator.cpp
|
||||
controls/QskPageIndicatorSkinlet.cpp
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskModelObjectBinder.h"
|
||||
|
||||
#include <qmetaobject.h>
|
||||
#include <qabstractitemmodel.h>
|
||||
#include <qpointer.h>
|
||||
|
||||
static inline QMetaProperty qskMetaProperty(
|
||||
const QMetaObject* metaObject, const QByteArray& name )
|
||||
{
|
||||
if ( name.isEmpty() )
|
||||
return metaObject->userProperty();
|
||||
|
||||
const auto idx = metaObject->indexOfProperty( name );
|
||||
return ( idx >= 0 ) ? metaObject->property( idx ) : QMetaProperty();
|
||||
}
|
||||
|
||||
static void qskEnableConnections( QObject* object,
|
||||
QskModelObjectBinder* binder, bool on )
|
||||
{
|
||||
if ( on )
|
||||
{
|
||||
QObject::connect( object, &QObject::destroyed,
|
||||
binder, &QskModelObjectBinder::unbindObject );
|
||||
}
|
||||
else
|
||||
{
|
||||
QObject::disconnect( object, &QObject::destroyed,
|
||||
binder, &QskModelObjectBinder::unbindObject );
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Binding
|
||||
{
|
||||
Binding( int column, const QMetaProperty& property )
|
||||
: property( property )
|
||||
, column( column )
|
||||
{
|
||||
}
|
||||
|
||||
QMetaProperty property;
|
||||
int column;
|
||||
};
|
||||
}
|
||||
|
||||
class QskModelObjectBinder::PrivateData
|
||||
{
|
||||
public:
|
||||
void updateProperties()
|
||||
{
|
||||
if ( model && currentRowIndex.isValid() )
|
||||
{
|
||||
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
|
||||
updateObjectProperty( it.key(), it.value() );
|
||||
}
|
||||
}
|
||||
|
||||
void updateProperties( const QModelIndex& topLeft,
|
||||
const QModelIndex& bottomRight, const QVector< int >& roles )
|
||||
{
|
||||
if ( !( model && currentRowIndex.isValid() ) )
|
||||
return;
|
||||
|
||||
if ( !( roles.isEmpty() || roles.contains( Qt::EditRole ) ) )
|
||||
return;
|
||||
|
||||
const int row = currentRowIndex.row();
|
||||
|
||||
if ( topLeft.row() <= row && row <= bottomRight.row() )
|
||||
{
|
||||
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
|
||||
{
|
||||
const int col = it.value().column;
|
||||
|
||||
if( topLeft.column() <= col && col <= bottomRight.column() )
|
||||
updateObjectProperty( it.key(), it.value() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateObjectProperty( QObject* object, const Binding& binding )
|
||||
{
|
||||
const auto index = model->index( currentRowIndex.row(), binding.column );
|
||||
binding.property.write( object, index.data( Qt::EditRole ) );
|
||||
}
|
||||
|
||||
public:
|
||||
QMap< QObject*, Binding > bindings;
|
||||
QPointer< QAbstractItemModel > model;
|
||||
QPersistentModelIndex currentRowIndex;
|
||||
};
|
||||
|
||||
QskModelObjectBinder::QskModelObjectBinder( QObject* parent )
|
||||
: QObject( parent )
|
||||
, m_data( new PrivateData() )
|
||||
{
|
||||
}
|
||||
|
||||
QskModelObjectBinder::QskModelObjectBinder( QAbstractItemModel* model, QObject* parent )
|
||||
: QskModelObjectBinder( parent )
|
||||
{
|
||||
setModel( model );
|
||||
}
|
||||
|
||||
QskModelObjectBinder::~QskModelObjectBinder()
|
||||
{
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::bindObject(
|
||||
QObject* object, int column, const QByteArray& propertyName )
|
||||
{
|
||||
if( object == nullptr )
|
||||
return;
|
||||
|
||||
// does not work with dynamic properties ...
|
||||
|
||||
const auto metaProperty = qskMetaProperty( object->metaObject(), propertyName );
|
||||
Q_ASSERT( metaProperty.isValid() );
|
||||
|
||||
if ( metaProperty.isValid() )
|
||||
{
|
||||
const Binding binding = { column, metaProperty };
|
||||
|
||||
m_data->bindings.insert( object, binding );
|
||||
qskEnableConnections( object, this, true );
|
||||
|
||||
if ( m_data->model && m_data->currentRowIndex.isValid() )
|
||||
m_data->updateObjectProperty( object, binding );
|
||||
}
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::unbindObject( QObject* object )
|
||||
{
|
||||
auto& bindings = m_data->bindings;
|
||||
|
||||
auto it = bindings.find( object );
|
||||
if ( it != bindings.end() )
|
||||
{
|
||||
qskEnableConnections( object, this, false );
|
||||
bindings.erase( it );
|
||||
}
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::clearBindings()
|
||||
{
|
||||
auto& bindings = m_data->bindings;
|
||||
|
||||
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
|
||||
qskEnableConnections( it.key(), this, false );
|
||||
|
||||
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 )
|
||||
{
|
||||
if ( model == m_data->model )
|
||||
return;
|
||||
|
||||
if ( m_data->model )
|
||||
{
|
||||
disconnect( m_data->model, &QAbstractItemModel::dataChanged, this, nullptr );
|
||||
|
||||
m_data->model = nullptr;
|
||||
m_data->currentRowIndex = QModelIndex();
|
||||
|
||||
clearBindings();
|
||||
}
|
||||
|
||||
m_data->model = model;
|
||||
|
||||
if( model )
|
||||
{
|
||||
auto updateProperties = [this](
|
||||
const QModelIndex& topLeft, const QModelIndex& bottomRight,
|
||||
const QVector< int >& roles )
|
||||
{
|
||||
m_data->updateProperties( topLeft, bottomRight, roles );
|
||||
};
|
||||
|
||||
connect( m_data->model, &QAbstractItemModel::dataChanged,
|
||||
this, updateProperties );
|
||||
|
||||
connect( m_data->model, &QObject::destroyed,
|
||||
this, &QskModelObjectBinder::clearBindings );
|
||||
|
||||
setCurrentRow( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
const QAbstractItemModel* QskModelObjectBinder::model() const
|
||||
{
|
||||
return m_data->model;
|
||||
}
|
||||
|
||||
QAbstractItemModel* QskModelObjectBinder::model()
|
||||
{
|
||||
return m_data->model;
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::setCurrentRow( int row )
|
||||
{
|
||||
auto model = m_data->model.data();
|
||||
auto& bindings = m_data->bindings;
|
||||
|
||||
Q_ASSERT( model != nullptr );
|
||||
|
||||
if ( model && row >= 0 && row < model->rowCount() )
|
||||
{
|
||||
m_data->currentRowIndex = model->index( row, 0 );
|
||||
|
||||
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
|
||||
m_data->updateObjectProperty( it.key(), it.value() );
|
||||
|
||||
Q_EMIT currentRowChanged( row );
|
||||
}
|
||||
}
|
||||
|
||||
int QskModelObjectBinder::currentRow() const
|
||||
{
|
||||
return m_data->currentRowIndex.row();
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::submit()
|
||||
{
|
||||
if ( auto model = m_data->model )
|
||||
{
|
||||
const auto& bindings = m_data->bindings;
|
||||
|
||||
for ( auto it = bindings.begin(); it != bindings.end(); ++it )
|
||||
{
|
||||
const auto value = it.value().property.read( it.key() );
|
||||
|
||||
const auto row = m_data->currentRowIndex.row();
|
||||
const auto index = model->index( row, it.value().column );
|
||||
|
||||
model->setData( index, value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::revert()
|
||||
{
|
||||
m_data->updateProperties();
|
||||
}
|
||||
|
||||
#include "moc_QskModelObjectBinder.cpp"
|
|
@ -0,0 +1,61 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_MODEL_OBJECT_BINDER_H
|
||||
#define QSK_MODEL_OBJECT_BINDER_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
|
||||
#include <qobject.h>
|
||||
#include <qbytearray.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QAbstractItemModel;
|
||||
class QMetaProperty;
|
||||
|
||||
class QSK_EXPORT QskModelObjectBinder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY( int currentRow READ currentRow
|
||||
WRITE setCurrentRow NOTIFY currentRowChanged )
|
||||
|
||||
public:
|
||||
QskModelObjectBinder( QObject* parent = nullptr );
|
||||
QskModelObjectBinder( QAbstractItemModel*, QObject* parent = nullptr );
|
||||
|
||||
~QskModelObjectBinder() override;
|
||||
|
||||
void setModel( QAbstractItemModel* );
|
||||
|
||||
const QAbstractItemModel* model() const;
|
||||
QAbstractItemModel* model();
|
||||
|
||||
void setCurrentRow( int row );
|
||||
int currentRow() const;
|
||||
|
||||
void bindObject( QObject*, int column,
|
||||
const QByteArray& propertyName = QByteArray() );
|
||||
|
||||
void unbindObject( QObject* );
|
||||
|
||||
QMetaProperty boundProperty( const QObject* ) const;
|
||||
QObjectList boundObjects() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void currentRowChanged( int );
|
||||
|
||||
public Q_SLOTS:
|
||||
void submit();
|
||||
void revert();
|
||||
void clearBindings();
|
||||
|
||||
private:
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue