mirror of https://github.com/KLayout/klayout.git
Implemented a solution for issue #1348 (Reload Files popup prevents closing KLayout): now the reload notification is shown as a notification bar at the top of the layout view
This commit is contained in:
parent
7169910607
commit
d40ed70fb9
|
|
@ -590,12 +590,7 @@ MainWindow::file_changed_timer ()
|
|||
|
||||
}
|
||||
|
||||
// Prevent recursive signals
|
||||
m_file_changed_timer.blockSignals (true);
|
||||
|
||||
std::set<QString> reloaded_files;
|
||||
|
||||
BEGIN_PROTECTED
|
||||
std::set<QString> notified_files;
|
||||
|
||||
// Make the names unique
|
||||
std::sort (m_changed_files.begin (), m_changed_files.end ());
|
||||
|
|
@ -605,68 +600,38 @@ BEGIN_PROTECTED
|
|||
return;
|
||||
}
|
||||
|
||||
QString msg;
|
||||
|
||||
if (m_changed_files.size () == 1) {
|
||||
msg = QObject::tr ("The following file has been changed on disk:\n\n");
|
||||
for (std::vector<QString>::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1\n").arg (*f);
|
||||
}
|
||||
msg += tr ("\nReload this file?");
|
||||
} else {
|
||||
msg = QObject::tr ("The following files have been changed on disk:\n\n");
|
||||
for (std::vector<QString>::const_iterator f = m_changed_files.begin (); f != m_changed_files.end (); ++f) {
|
||||
msg += QString::fromUtf8 (" %1\n").arg (*f);
|
||||
}
|
||||
msg += tr ("\nReload these files?");
|
||||
}
|
||||
// adds a notification to the involved views that the file has changed
|
||||
|
||||
std::vector<QString> changed_files;
|
||||
changed_files.swap (m_changed_files);
|
||||
|
||||
if (QMessageBox::question (this, tr ("Reload Files"), msg, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
std::map<QString, std::vector<std::pair<lay::LayoutViewWidget *, int> > > views_per_file;
|
||||
|
||||
m_file_changed_timer.blockSignals (false);
|
||||
|
||||
std::map<QString, std::pair<lay::LayoutViewWidget *, int> > views_per_file;
|
||||
|
||||
for (std::vector<lay::LayoutViewWidget *>::iterator v = mp_views.begin (); v != mp_views.end (); ++v) {
|
||||
for (int cv = 0; cv < int ((*v)->view ()->cellviews ()); ++cv) {
|
||||
views_per_file [tl::to_qstring ((*v)->view ()->cellview (cv)->filename ())] = std::make_pair (*v, cv);
|
||||
}
|
||||
for (auto v = mp_views.begin (); v != mp_views.end (); ++v) {
|
||||
for (int cv = 0; cv < int ((*v)->view ()->cellviews ()); ++cv) {
|
||||
views_per_file [tl::to_qstring ((*v)->view ()->cellview (cv)->filename ())].push_back (std::make_pair (*v, cv));
|
||||
}
|
||||
|
||||
for (std::vector<QString>::const_iterator f = changed_files.begin (); f != changed_files.end (); ++f) {
|
||||
std::map<QString, std::pair<lay::LayoutViewWidget *, int> >::const_iterator v = views_per_file.find (*f);
|
||||
if (v != views_per_file.end ()) {
|
||||
v->second.first->view ()->reload_layout (v->second.second);
|
||||
reloaded_files.insert (*f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
END_PROTECTED
|
||||
for (auto f = changed_files.begin (); f != changed_files.end (); ++f) {
|
||||
|
||||
m_file_changed_timer.blockSignals (false);
|
||||
auto v = views_per_file.find (*f);
|
||||
if (v != views_per_file.end ()) {
|
||||
|
||||
// While the message box was open, new request might have collected - remove
|
||||
// the ones we just reloaded and restart the timer
|
||||
if (! m_changed_files.empty ()) {
|
||||
for (auto w = v->second.begin (); w != v->second.end (); ++w) {
|
||||
|
||||
std::string title;
|
||||
if (w->first->view ()->cellviews () > 1) {
|
||||
title = tl::sprintf (tl::to_string (tr ("Layout file @%d (%s) has changed on disk")), w->second + 1, tl::filename (tl::to_string (*f)));
|
||||
} else {
|
||||
title = tl::sprintf (tl::to_string (tr ("Layout file (%s) has changed on disk")), tl::filename (tl::to_string (*f)));
|
||||
}
|
||||
|
||||
lay::LayoutViewNotification n ("reload", title, tl::Variant (tl::to_string (*f)));
|
||||
n.add_action ("reload", tl::to_string (tr ("Reload")));
|
||||
w->first->add_notification (n);
|
||||
|
||||
std::vector<QString> changed_files;
|
||||
changed_files.swap (m_changed_files);
|
||||
for (std::vector<QString>::const_iterator f = changed_files.begin (); f != changed_files.end (); ++f) {
|
||||
if (reloaded_files.find (*f) == reloaded_files.end ()) {
|
||||
m_changed_files.push_back (*f);
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_changed_files.empty ()) {
|
||||
|
||||
// Wait a little to let more to allow for more reload requests to collect
|
||||
m_file_changed_timer.setInterval (300);
|
||||
m_file_changed_timer.start ();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QApplication>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
|
||||
#include "tlInternational.h"
|
||||
#include "tlExpression.h"
|
||||
|
|
@ -76,6 +78,7 @@
|
|||
#include "layEditorOptionsPages.h"
|
||||
#include "layUtils.h"
|
||||
#include "layPropertiesDialog.h"
|
||||
#include "layQtTools.h"
|
||||
#include "dbClipboard.h"
|
||||
#include "dbLayout.h"
|
||||
#include "dbLayoutUtils.h"
|
||||
|
|
@ -94,12 +97,70 @@
|
|||
namespace lay
|
||||
{
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// LayoutViewNotificationWidget implementation
|
||||
|
||||
LayoutViewNotificationWidget::LayoutViewNotificationWidget (LayoutViewWidget *parent, const LayoutViewNotification *notification)
|
||||
: QFrame (parent), mp_parent (parent), mp_notification (notification)
|
||||
{
|
||||
setBackgroundRole (QPalette::ToolTipBase);
|
||||
setAutoFillBackground (true);
|
||||
|
||||
QHBoxLayout *layout = new QHBoxLayout (this);
|
||||
layout->setContentsMargins (4, 4, 4, 4);
|
||||
|
||||
QLabel *title_label = new QLabel (this);
|
||||
layout->addWidget (title_label, 1);
|
||||
title_label->setText (tl::to_qstring (notification->title ()));
|
||||
title_label->setForegroundRole (QPalette::ToolTipText);
|
||||
title_label->setWordWrap (true);
|
||||
activate_help_links (title_label);
|
||||
|
||||
for (auto a = notification->actions ().begin (); a != notification->actions ().end (); ++a) {
|
||||
|
||||
QPushButton *pb = new QPushButton (this);
|
||||
layout->addWidget (pb);
|
||||
|
||||
pb->setText (tl::to_qstring (a->second));
|
||||
m_action_buttons.insert (std::make_pair (pb, a->first));
|
||||
connect (pb, SIGNAL (clicked ()), this, SLOT (action_triggered ()));
|
||||
|
||||
}
|
||||
|
||||
QToolButton *close_button = new QToolButton ();
|
||||
close_button->setIcon (QIcon (":clear_edit_16px.png"));
|
||||
close_button->setAutoRaise (true);
|
||||
layout->addWidget (close_button);
|
||||
|
||||
connect (close_button, SIGNAL (clicked ()), this, SLOT (close_triggered ()));
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewNotificationWidget::action_triggered ()
|
||||
{
|
||||
auto a = m_action_buttons.find (sender ());
|
||||
if (a != m_action_buttons.end ()) {
|
||||
mp_parent->notification_action (*mp_notification, a->second);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewNotificationWidget::close_triggered ()
|
||||
{
|
||||
mp_parent->remove_notification (*mp_notification);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// LayoutViewWidget implementation
|
||||
|
||||
LayoutViewWidget::LayoutViewWidget (db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, QWidget *parent, unsigned int options)
|
||||
: QFrame (parent), mp_view (0)
|
||||
{
|
||||
mp_layout = new QVBoxLayout (this);
|
||||
mp_layout->setContentsMargins (0, 0, 0, 0);
|
||||
mp_layout->setSpacing (0);
|
||||
mp_layout->addStretch (1);
|
||||
|
||||
// NOTE: construction the LayoutView may trigger events (script code executed etc.) which must
|
||||
// not meet an invalid mp_view pointer (e.g. in eventFilter). Hence, mp_view is 0 first, and set only
|
||||
// after the LayoutView is successfully constructed.
|
||||
|
|
@ -110,6 +171,11 @@ LayoutViewWidget::LayoutViewWidget (db::Manager *mgr, bool editable, lay::Plugin
|
|||
LayoutViewWidget::LayoutViewWidget (lay::LayoutView *source, db::Manager *mgr, bool editable, lay::Plugin *plugin_parent, QWidget *parent, unsigned int options)
|
||||
: QFrame (parent), mp_view (0)
|
||||
{
|
||||
mp_layout = new QVBoxLayout (this);
|
||||
mp_layout->setContentsMargins (0, 0, 0, 0);
|
||||
mp_layout->setSpacing (0);
|
||||
mp_layout->addStretch (1);
|
||||
|
||||
// NOTE: construction the LayoutView may trigger events (script code executed etc.) which must
|
||||
// not meet an invalid mp_view pointer (e.g. in eventFilter). Hence, mp_view is 0 first, and set only
|
||||
// after the LayoutView is successfully constructed.
|
||||
|
|
@ -124,6 +190,55 @@ LayoutViewWidget::~LayoutViewWidget ()
|
|||
delete view;
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewWidget::add_notification (const LayoutViewNotification ¬ificaton)
|
||||
{
|
||||
if (m_notification_widgets.find (¬ificaton) == m_notification_widgets.end ()) {
|
||||
m_notifications.push_back (notificaton);
|
||||
QWidget *w = new LayoutViewNotificationWidget (this, &m_notifications.back ());
|
||||
m_notification_widgets.insert (std::make_pair (&m_notifications.back (), w));
|
||||
mp_layout->insertWidget (0, w);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewWidget::remove_notification (const LayoutViewNotification ¬ification)
|
||||
{
|
||||
auto nw = m_notification_widgets.find (¬ification);
|
||||
if (nw != m_notification_widgets.end ()) {
|
||||
|
||||
nw->second->deleteLater ();
|
||||
m_notification_widgets.erase (nw);
|
||||
|
||||
for (auto n = m_notifications.begin (); n != m_notifications.end (); ++n) {
|
||||
if (*n == notification) {
|
||||
m_notifications.erase (n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewWidget::notification_action (const LayoutViewNotification ¬ification, const std::string &action)
|
||||
{
|
||||
if (action == "reload") {
|
||||
|
||||
std::string fn = notification.parameter ().to_string ();
|
||||
|
||||
for (unsigned int cvi = 0; cvi < mp_view->cellviews (); ++cvi) {
|
||||
const lay::CellView &cv = mp_view->cellview (cvi);
|
||||
if (cv->filename () == fn) {
|
||||
mp_view->reload_layout (cvi);
|
||||
}
|
||||
}
|
||||
|
||||
remove_notification (notification);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewWidget::view_deleted (lay::LayoutView *view)
|
||||
{
|
||||
|
|
@ -135,6 +250,14 @@ LayoutViewWidget::view_deleted (lay::LayoutView *view)
|
|||
mp_view = new LayoutView (view->manager (), view->is_editable (), view->plugin_parent (), this, view->options ());
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewWidget::resizeEvent (QResizeEvent *)
|
||||
{
|
||||
if (mp_view && mp_view->canvas ()) {
|
||||
mp_view->canvas ()->resize (width (), height ());
|
||||
}
|
||||
}
|
||||
|
||||
QSize
|
||||
LayoutViewWidget::sizeHint () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
|
||||
#include <QImage>
|
||||
#include <QPointer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class QSpinBox;
|
||||
|
||||
|
|
@ -683,6 +684,91 @@ private:
|
|||
void activate_editor_option_pages ();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Descriptor for a notification inside the layout view
|
||||
*
|
||||
* Notifications are popups added at the top of the view to indicate need for reloading for example.
|
||||
* Notifications have a name, a title, optional actions (id, title) and a parameter (e.g. file path to reload).
|
||||
* Actions are mapped to QPushButtons.
|
||||
*/
|
||||
class LAYVIEW_PUBLIC LayoutViewNotification
|
||||
{
|
||||
public:
|
||||
LayoutViewNotification (const std::string &name, const std::string &title, const tl::Variant ¶meter = tl::Variant ())
|
||||
: m_name (name), m_title (title), m_parameter (parameter)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void add_action (const std::string &name, const std::string &title)
|
||||
{
|
||||
m_actions.push_back (std::make_pair (name, title));
|
||||
}
|
||||
|
||||
const std::vector<std::pair<std::string, std::string> > &actions () const
|
||||
{
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
const std::string &name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
const std::string &title () const
|
||||
{
|
||||
return m_title;
|
||||
}
|
||||
|
||||
const tl::Variant ¶meter () const
|
||||
{
|
||||
return m_parameter;
|
||||
}
|
||||
|
||||
bool operator<(const LayoutViewNotification &other) const
|
||||
{
|
||||
if (m_name != other.name ()) {
|
||||
return m_name < other.name ();
|
||||
}
|
||||
return m_parameter < other.parameter ();
|
||||
}
|
||||
|
||||
bool operator==(const LayoutViewNotification &other) const
|
||||
{
|
||||
if (m_name != other.name ()) {
|
||||
return false;
|
||||
}
|
||||
return m_parameter == other.parameter ();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_title;
|
||||
tl::Variant m_parameter;
|
||||
std::vector<std::pair<std::string, std::string> > m_actions;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A widget representing a notification
|
||||
*/
|
||||
class LAYVIEW_PUBLIC LayoutViewNotificationWidget
|
||||
: public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LayoutViewNotificationWidget (LayoutViewWidget *parent, const LayoutViewNotification *notification);
|
||||
|
||||
private slots:
|
||||
void action_triggered ();
|
||||
void close_triggered ();
|
||||
|
||||
private:
|
||||
LayoutViewWidget *mp_parent;
|
||||
const LayoutViewNotification *mp_notification;
|
||||
std::map<QObject *, std::string> m_action_buttons;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The layout view widget
|
||||
*
|
||||
|
|
@ -710,6 +796,16 @@ public:
|
|||
*/
|
||||
~LayoutViewWidget ();
|
||||
|
||||
/**
|
||||
* @brief Adds a notification
|
||||
*/
|
||||
void add_notification (const LayoutViewNotification ¬ificaton);
|
||||
|
||||
/**
|
||||
* @brief Removes a notification
|
||||
*/
|
||||
void remove_notification (const LayoutViewNotification ¬ificaton);
|
||||
|
||||
/**
|
||||
* @brief Gets the LayoutView embedded into this widget
|
||||
*/
|
||||
|
|
@ -817,10 +913,25 @@ signals:
|
|||
|
||||
private:
|
||||
friend class LayoutView;
|
||||
friend class LayoutViewNotificationWidget;
|
||||
|
||||
void view_deleted (lay::LayoutView *view);
|
||||
void notification_action (const LayoutViewNotification ¬ification, const std::string &action);
|
||||
|
||||
void resizeEvent (QResizeEvent *event);
|
||||
|
||||
struct CompareNotificationPointers
|
||||
{
|
||||
bool operator() (const LayoutViewNotification *a, const LayoutViewNotification *b) const
|
||||
{
|
||||
return *a < *b;
|
||||
}
|
||||
};
|
||||
|
||||
LayoutView *mp_view;
|
||||
QVBoxLayout *mp_layout;
|
||||
std::list<lay::LayoutViewNotification> m_notifications;
|
||||
std::map<const LayoutViewNotification *, QWidget *, CompareNotificationPointers> m_notification_widgets;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue