WIP: multi selection property dialog

This commit is contained in:
Matthias Koefferlein 2022-10-08 00:37:39 +02:00
parent 43c7e87cc0
commit 187aae2649
9 changed files with 278 additions and 86 deletions

View File

@ -86,12 +86,59 @@ ShapePropertiesPage::select_entries (const std::vector<size_t> &entries)
m_index = entries.front ();
}
lay::LayoutViewBase *
ShapePropertiesPage::view () const
{
return mp_service->view ();
}
const db::Shape &
ShapePropertiesPage::shape (size_t entry) const
{
return m_selection_ptrs [entry]->shape ();
}
double
ShapePropertiesPage::dbu (size_t entry) const
{
unsigned int cv_index = m_selection_ptrs [entry]->cv_index ();
return view ()->cellview (cv_index)->layout ().dbu ();
}
std::string
ShapePropertiesPage::description (size_t entry) const
{
return m_selection_ptrs [entry]->shape ().to_string (); // @@@
unsigned int cv_index = m_selection_ptrs [entry]->cv_index ();
unsigned int layer = m_selection_ptrs [entry]->layer ();
if (view ()->cellview (cv_index).is_valid ()) {
const db::LayerProperties &lp = view ()->cellview (cv_index)->layout ().get_properties (layer);
if (view ()->cellviews () > 1) {
return lp.to_string () + "@" + tl::to_string (cv_index + 1);
} else {
return lp.to_string ();
}
}
return std::string ();
}
QIcon
ShapePropertiesPage::icon (size_t entry, int w, int h) const
{
int cv_index = m_selection_ptrs [entry]->cv_index ();
int layer = m_selection_ptrs [entry]->layer ();
auto *view = mp_service->view ();
for (auto lp = view->begin_layers (view->current_layer_list ()); ! lp.at_end (); ++lp) {
const lay::LayerPropertiesNode *ln = lp.operator-> ();
if (ln->cellview_index () == cv_index && ln->layer_index () == layer) {
return QIcon (QPixmap::fromImage (view->icon_for_layer (lp, w, h).to_image ()));
}
}
return QIcon ();
}
std::string
ShapePropertiesPage::description () const
{
@ -394,7 +441,32 @@ PolygonPropertiesPage::PolygonPropertiesPage (edt::Service *service, db::Manager
}
}
void
static size_t count_polygon_points (const db::Shape &sh)
{
size_t n = 0;
for (auto pt = sh.begin_hull (); pt != sh.end_hull (); ++pt) {
++n;
}
return n;
}
std::string
PolygonPropertiesPage::description (size_t entry) const
{
const db::Shape &sh = shape (entry);
size_t npts = count_polygon_points (sh);
if (sh.holes () == 0 && npts > 4) {
return ShapePropertiesPage::description () + " " + tl::sprintf (tl::to_string (tr ("Polygon(%d points)")), npts);
} else if (sh.holes () > 0) {
return ShapePropertiesPage::description () + " " + tl::sprintf (tl::to_string (tr ("Polygon(%d points, %d holes)")), npts, sh.holes ());
} else {
db::CplxTrans dbu_trans (dbu (entry));
return ShapePropertiesPage::description () + " " + tl::sprintf (tl::to_string (tr ("Polygon(%s)")), (dbu_trans * sh.polygon ()).to_string ());
}
}
void
PolygonPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname)
{
layer_lbl->setText (tl::to_qstring (lname));
@ -562,6 +634,14 @@ BoxPropertiesPage::BoxPropertiesPage (edt::Service *service, db::Manager *manage
connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ()));
}
std::string
BoxPropertiesPage::description (size_t entry) const
{
const db::Shape &sh = shape (entry);
db::CplxTrans dbu_trans (dbu (entry));
return ShapePropertiesPage::description () + " " + tl::sprintf (tl::to_string (tr ("Box(%s)")), (dbu_trans * sh.box ()).to_string ());
}
void
BoxPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname)
{
@ -793,7 +873,15 @@ TextPropertiesPage::TextPropertiesPage (edt::Service *service, db::Manager *mana
}
}
void
std::string
TextPropertiesPage::description (size_t entry) const
{
const db::Shape &sh = shape (entry);
db::CplxTrans dbu_trans (dbu (entry));
return ShapePropertiesPage::description () + " " + tl::sprintf (tl::to_string (tr ("Text(%s)")), (dbu_trans * sh.text ()).to_string ());
}
void
TextPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname)
{
layer_lbl->setText (tl::to_qstring (lname));
@ -917,6 +1005,32 @@ PathPropertiesPage::PathPropertiesPage (edt::Service *service, db::Manager *mana
round_cb->setEnabled (false);
}
static size_t count_path_points (const db::Shape &sh)
{
size_t n = 0;
for (auto pt = sh.begin_point (); pt != sh.end_point (); ++pt) {
++n;
}
return n;
}
static std::string path_description (const db::Shape &sh, double dbu)
{
size_t npts = count_path_points (sh);
if (npts > 4) {
return tl::sprintf (tl::to_string (tr ("Path(%d points, w=%s)")), npts, tl::micron_to_string (sh.path_width () * dbu));
} else {
db::CplxTrans dbu_trans (dbu);
return tl::sprintf (tl::to_string (tr ("Path(%s)")), (dbu_trans * sh.path ()).to_string ());
}
}
std::string
PathPropertiesPage::description (size_t entry) const
{
return ShapePropertiesPage::description () + " " + path_description (shape (entry), dbu (entry));
}
void
PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname)
{
@ -1015,6 +1129,12 @@ EditablePathPropertiesPage::text_changed ()
m_in_text_changed = false;
}
std::string
EditablePathPropertiesPage::description (size_t entry) const
{
return ShapePropertiesPage::description () + " " + path_description (shape (entry), dbu (entry));
}
void
EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname)
{

View File

@ -52,6 +52,7 @@ public:
virtual size_t count () const;
virtual void select_entries (const std::vector<size_t> &entries);
virtual std::string description (size_t entry) const;
virtual QIcon icon (size_t entry, int w, int h) const;
virtual std::string description () const;
virtual void leave ();
@ -82,6 +83,9 @@ protected:
bool abs_trans () const;
db::ICplxTrans trans () const;
void setup ();
lay::LayoutViewBase *view () const;
const db::Shape &shape (size_t entry) const;
double dbu (size_t entry) const;
public slots:
void show_inst ();
@ -100,6 +104,7 @@ Q_OBJECT
public:
PolygonPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent);
virtual std::string description (size_t entry) const;
virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname);
virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu);
@ -123,6 +128,7 @@ Q_OBJECT
public:
BoxPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent);
virtual std::string description (size_t entry) const;
virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname);
virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu);
@ -152,6 +158,7 @@ Q_OBJECT
public:
TextPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent);
virtual std::string description (size_t entry) const;
virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname);
virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu);
@ -169,6 +176,7 @@ Q_OBJECT
public:
PathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent);
virtual std::string description (size_t entry) const;
virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname);
virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu);
@ -189,6 +197,7 @@ Q_OBJECT
public:
EditablePathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent);
virtual std::string description (size_t entry) const;
virtual void do_update (const db::Shape &shape, double dbu, const std::string &lname);
virtual ChangeApplicator *create_applicator (db::Shapes &shapes, const db::Shape &shape, double dbu);

View File

@ -198,20 +198,6 @@ LayoutCanvas::~LayoutCanvas ()
clear_fg_bitmaps ();
}
#if defined(HAVE_QT) && QT_VERSION >= 0x050000
double
LayoutCanvas::dpr () const
{
return widget () ? widget ()->devicePixelRatio () : 1.0;
}
#else
double
LayoutCanvas::dpr () const
{
return 1.0;
}
#endif
double
LayoutCanvas::resolution () const
{
@ -297,6 +283,16 @@ LayoutCanvas::set_highres_mode (bool hrm)
}
}
double
LayoutCanvas::dpr () const
{
#if defined(HAVE_QT) && QT_VERSION >= 0x50000
return widget () ? widget ()->devicePixelRatio () : 1.0;
#else
return 1.0;
#endif
}
void
LayoutCanvas::set_colors (tl::Color background, tl::Color foreground, tl::Color active)
{

View File

@ -277,6 +277,11 @@ public:
return m_hrm;
}
/**
* @brief Gets the default device pixel ratio for this canvas
*/
double dpr () const;
/**
* @brief Sets the depth of the image cache
*/
@ -439,7 +444,6 @@ private:
void do_redraw_all (bool force_redraw = true);
void prepare_drawing ();
double dpr () const;
virtual double resolution () const;
const std::vector<ViewOp> &scaled_view_ops (unsigned int lw);

View File

@ -1548,6 +1548,10 @@ single_bitmap_to_image (const lay::ViewOp &view_op, lay::Bitmap &bitmap,
tl::PixelBuffer
LayoutViewBase::icon_for_layer (const LayerPropertiesConstIterator &iter, unsigned int w, unsigned int h, double dpr, unsigned int di_off, bool no_state)
{
if (dpr < 0.0) {
dpr = canvas ()->dpr ();
}
int oversampling = canvas () ? canvas ()->oversampling () : 1;
double gamma = 2.0;

View File

@ -587,11 +587,11 @@ public:
* @param iter indicates the layer
* @param w The width in logical pixels of the generated pixmap (will be multiplied by dpr)
* @param h The height in logical pixels of the generated pixmap (will be multiplied by dpr)
* @param dpr The device pixel ratio (number of image pixes per logical pixel)
* @param dpr The device pixel ratio (number of image pixes per logical pixel) - negative values mean auto-detect
* @param di_off The dither pattern offset (used for animation)
* @param no_state If true, the state will not be indicated
*/
tl::PixelBuffer icon_for_layer (const lay::LayerPropertiesConstIterator &iter, unsigned int w, unsigned int h, double dpr, unsigned int di_off, bool no_state);
tl::PixelBuffer icon_for_layer (const lay::LayerPropertiesConstIterator &iter, unsigned int w, unsigned int h, double dpr = -1.0, unsigned int di_off = 0, bool no_state = false);
/**
* @brief Sets the layers that are selected in the layer browser

View File

@ -29,6 +29,7 @@
#include "layEditable.h"
#include <QFrame>
#include <QIcon>
namespace db
{
@ -97,11 +98,27 @@ public:
*/
virtual std::string description (size_t entry) const = 0;
/**
* @brief Gets the icon for the nth entry
*/
virtual QIcon icon (size_t /*entry*/, int /*w*/, int /*h*/) const
{
return QIcon ();
}
/**
* @brief Gets a description text for the whole group
*/
virtual std::string description () const = 0;
/**
* @brief Gets the icon associated with the whole group
*/
virtual QIcon icon (int /*w*/, int /*h*/) const
{
return QIcon ();
}
/**
* @brief Update the display
*

View File

@ -44,8 +44,8 @@ class PropertiesTreeModel
: public QAbstractItemModel
{
public:
PropertiesTreeModel (PropertiesDialog *dialog)
: QAbstractItemModel (dialog), mp_dialog (dialog)
PropertiesTreeModel (PropertiesDialog *dialog, int icon_width, int icon_height)
: QAbstractItemModel (dialog), mp_dialog (dialog), m_icon_width (icon_width), m_icon_height (icon_height)
{ }
int columnCount (const QModelIndex &) const
@ -61,6 +61,16 @@ public:
} else {
return tl::to_qstring (mp_dialog->properties_pages () [index.row ()]->description ());
}
} else if (role == Qt::DecorationRole) {
QIcon icon;
if (index.internalId () < mp_dialog->properties_pages ().size ()) {
icon = mp_dialog->properties_pages () [index.internalId ()]->icon (index.row (), m_icon_width, m_icon_height);
} else {
icon = mp_dialog->properties_pages () [index.internalId ()]->icon (m_icon_width, m_icon_height);
}
if (! icon.isNull ()) {
return QVariant (icon);
}
}
return QVariant ();
}
@ -68,7 +78,7 @@ public:
Qt::ItemFlags flags (const QModelIndex &index) const
{
Qt::ItemFlags f = QAbstractItemModel::flags (index);
if (index.internalId () >= mp_dialog->properties_pages ().size ()) {
if (index.internalId () >= mp_dialog->properties_pages ().size () && ! mp_dialog->properties_pages () [index.row ()]->can_apply_to_all ()) {
f &= ~Qt::ItemIsSelectable;
}
return f;
@ -120,11 +130,25 @@ public:
QModelIndex index_for (int page_index, int object_index)
{
return createIndex (object_index, 0, qint64 (page_index));
if (page_index < 0) {
return QModelIndex ();
} else {
return createIndex (object_index, 0, qint64 (page_index));
}
}
QModelIndex index_for (int page_index)
{
if (page_index < 0) {
return QModelIndex ();
} else {
return createIndex (page_index, 0, qint64 (mp_dialog->properties_pages ().size ()));
}
}
private:
PropertiesDialog *mp_dialog;
int m_icon_width, m_icon_height;
};
// ----------------------------------------------------------------------------------------------------------
@ -175,14 +199,6 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager,
mp_ui->content_frame->setLayout (mp_stack);
// disable the apply button for first ..
mp_ui->apply_to_all_cbx->setEnabled (false);
mp_ui->relative_cbx->setEnabled (false);
mp_ui->ok_button->setEnabled (false);
// as a proposal, the start button can be enabled in most cases
mp_ui->prev_button->setEnabled (true);
// count the total number of objects
m_objects = mp_editables->selection_size ();
m_current_object = 0;
@ -195,41 +211,26 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager,
update_title ();
// if at end disable the "Next" button and return (this may only happen at the first call)
if (m_index < 0) {
mp_tree_model = new PropertiesTreeModel (this, mp_ui->tree->iconSize ().width (), mp_ui->tree->iconSize ().height ());
mp_ui->tree->setModel (mp_tree_model);
mp_ui->tree->expandAll ();
mp_ui->prev_button->setEnabled (false);
mp_ui->next_button->setEnabled (false);
mp_stack->setCurrentWidget (mp_none);
mp_ui->apply_to_all_cbx->setEnabled (false);
mp_ui->apply_to_all_cbx->setChecked (false);
mp_ui->relative_cbx->setEnabled (false);
mp_ui->relative_cbx->setChecked (false);
mp_ui->ok_button->setEnabled (false);
mp_ui->tree->setEnabled (false);
m_signals_enabled = false;
mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index, m_object_index));
m_signals_enabled = true;
} else {
mp_tree_model = new PropertiesTreeModel (this);
mp_ui->tree->setModel (mp_tree_model);
mp_ui->tree->expandAll ();
m_signals_enabled = false;
mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index, m_object_index));
m_signals_enabled = true;
update_controls ();
update_controls ();
// @@@ save this status!
mp_ui->apply_to_all_cbx->setChecked (false);
mp_ui->relative_cbx->setChecked (true);
mp_ui->apply_to_all_cbx->setChecked (false);
mp_ui->relative_cbx->setChecked (true);
connect (mp_ui->ok_button, SIGNAL (clicked ()), this, SLOT (ok_pressed ()));
connect (mp_ui->cancel_button, SIGNAL (clicked ()), this, SLOT (cancel_pressed ()));
connect (mp_ui->prev_button, SIGNAL (clicked ()), this, SLOT (prev_pressed ()));
connect (mp_ui->next_button, SIGNAL (clicked ()), this, SLOT (next_pressed ()));
connect (mp_ui->tree->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_index_changed (const QModelIndex &, const QModelIndex &)));
}
connect (mp_ui->ok_button, SIGNAL (clicked ()), this, SLOT (ok_pressed ()));
connect (mp_ui->cancel_button, SIGNAL (clicked ()), this, SLOT (cancel_pressed ()));
connect (mp_ui->prev_button, SIGNAL (clicked ()), this, SLOT (prev_pressed ()));
connect (mp_ui->next_button, SIGNAL (clicked ()), this, SLOT (next_pressed ()));
connect (mp_ui->apply_to_all_cbx, SIGNAL (clicked ()), this, SLOT (apply_to_all_pressed ()));
connect (mp_ui->tree->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_index_changed (const QModelIndex &, const QModelIndex &)));
}
PropertiesDialog::~PropertiesDialog ()
@ -251,51 +252,91 @@ PropertiesDialog::disconnect ()
mp_properties_pages.clear ();
}
void
PropertiesDialog::apply_to_all_pressed ()
{
m_signals_enabled = false;
if (mp_ui->apply_to_all_cbx->isChecked ()) {
mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index));
} else {
mp_ui->tree->setCurrentIndex (mp_tree_model->index_for (m_index, m_object_index));
}
m_signals_enabled = true;
}
void
PropertiesDialog::current_index_changed (const QModelIndex &index, const QModelIndex & /*previous*/)
{
if (m_signals_enabled && index.isValid ()) {
if (! m_signals_enabled) {
return;
}
if (mp_tree_model->parent (index).isValid ()) {
if (! index.isValid ()) {
return;
}
m_index = mp_tree_model->page_index (index);
m_object_index = mp_tree_model->object_index (index);
if (mp_tree_model->parent (index).isValid ()) {
} else {
m_index = mp_tree_model->page_index (index);
mp_ui->apply_to_all_cbx->setChecked (false);
m_index = index.row ();
m_object_index = 0;
m_object_index = mp_tree_model->object_index (index);
}
} else {
m_current_object = 0;
for (int i = 0; i < m_index; ++i) {
m_current_object += mp_properties_pages [i]->count ();
}
m_current_object += m_object_index;
m_index = index.row ();
mp_ui->apply_to_all_cbx->setChecked (mp_properties_pages [m_index]->can_apply_to_all ());
update_title ();
update_controls ();
m_object_index = 0;
}
m_current_object = 0;
for (int i = 0; i < m_index; ++i) {
m_current_object += mp_properties_pages [i]->count ();
}
m_current_object += m_object_index;
update_title ();
update_controls ();
}
void
PropertiesDialog::update_controls ()
{
if (m_prev_index >= 0 && m_index != m_prev_index) {
mp_properties_pages [m_prev_index]->leave ();
if (m_prev_index >= 0 && m_prev_index < int (mp_properties_pages.size ())) {
mp_properties_pages [m_prev_index]->leave ();
}
}
m_prev_index = m_index;
mp_stack->setCurrentWidget (mp_properties_pages [m_index]);
mp_ui->prev_button->setEnabled (any_prev ());
mp_ui->next_button->setEnabled (any_next ());
mp_ui->apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ());
mp_ui->relative_cbx->setEnabled (mp_ui->apply_to_all_cbx->isEnabled () && mp_ui->apply_to_all_cbx->isChecked ());
mp_ui->ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ());
mp_properties_pages [m_index]->select_entry (m_object_index);
mp_properties_pages [m_index]->update ();
if (m_index < 0) {
mp_stack->setCurrentWidget (mp_none);
mp_ui->prev_button->setEnabled (false);
mp_ui->next_button->setEnabled (false);
mp_ui->apply_to_all_cbx->setEnabled (false);
mp_ui->relative_cbx->setEnabled (false);
mp_ui->ok_button->setEnabled (false);
mp_ui->tree->setEnabled (false);
} else {
mp_stack->setCurrentWidget (mp_properties_pages [m_index]);
mp_ui->prev_button->setEnabled (any_prev ());
mp_ui->next_button->setEnabled (any_next ());
mp_ui->apply_to_all_cbx->setEnabled (! mp_properties_pages [m_index]->readonly () && mp_properties_pages [m_index]->can_apply_to_all ());
mp_ui->relative_cbx->setEnabled (mp_ui->apply_to_all_cbx->isEnabled () && mp_ui->apply_to_all_cbx->isChecked ());
mp_ui->ok_button->setEnabled (! mp_properties_pages [m_index]->readonly ());
mp_ui->tree->setEnabled (true);
mp_properties_pages [m_index]->select_entry (m_object_index);
mp_properties_pages [m_index]->update ();
}
}
void

View File

@ -108,6 +108,7 @@ public slots:
void prev_pressed ();
void cancel_pressed ();
void ok_pressed ();
void apply_to_all_pressed ();
void current_index_changed (const QModelIndex &index, const QModelIndex &previous);
protected: