pv/widgets/colorpopup.cpp
pv/widgets/devicetoolbutton.cpp
pv/widgets/exportmenu.cpp
+ pv/widgets/flowlayout.cpp
pv/widgets/importmenu.cpp
pv/widgets/popup.cpp
pv/widgets/popuptoolbutton.cpp
pv/widgets/colorpopup.hpp
pv/widgets/devicetoolbutton.hpp
pv/widgets/exportmenu.hpp
+ pv/widgets/flowlayout.hpp
pv/widgets/importmenu.hpp
pv/widgets/popup.hpp
pv/widgets/popuptoolbutton.hpp
#include <pv/data/logicsegment.hpp>
#include <pv/widgets/decodergroupbox.hpp>
#include <pv/widgets/decodermenu.hpp>
+#include <pv/widgets/flowlayout.hpp>
using std::abs;
using std::find_if;
const int DecodeTrace::MaxTraceUpdateRate = 1; // No more than 1 Hz
const unsigned int DecodeTrace::AnimationDurationInTicks = 7;
+
+/**
+ * Helper function for forceUpdate()
+ */
+void invalidateLayout(QLayout* layout)
+{
+ // Recompute the given layout and all its child layouts recursively
+ for (int i = 0; i < layout->count(); i++) {
+ QLayoutItem *item = layout->itemAt(i);
+
+ if (item->layout())
+ invalidateLayout(item->layout());
+ else
+ item->invalidate();
+ }
+
+ layout->invalidate();
+ layout->activate();
+}
+
+void forceUpdate(QWidget* widget)
+{
+ // Update all child widgets recursively
+ for (QObject* child : widget->children())
+ if (child->isWidgetType())
+ forceUpdate((QWidget*)child);
+
+ // Invalidate the layout of the widget itself
+ if (widget->layout())
+ invalidateLayout(widget->layout());
+}
+
+
+ContainerWidget::ContainerWidget(QWidget *parent) :
+ QWidget(parent)
+{
+}
+
+void ContainerWidget::resizeEvent(QResizeEvent* event)
+{
+ QWidget::resizeEvent(event);
+
+ widgetResized(this);
+}
+
+
DecodeTrace::DecodeTrace(pv::Session &session,
shared_ptr<data::SignalBase> signalbase, int index) :
Trace(signalbase),
void DecodeTrace::mouse_left_press_event(const QMouseEvent* event)
{
+ // Update container widths which depend on the scrollarea's current width
+ update_expanded_rows();
+
// Handle row expansion marker
for (DecodeTraceRow& r : rows_) {
if (!r.expand_marker_highlighted)
} else {
r.expanding = true;
r.anim_shape = 0;
+
+ // Force geometry update of the widget container to get
+ // an up-to-date height (which also depends on the width)
+ forceUpdate(r.container);
+
r.container->setVisible(true);
- QApplication::processEvents();
- r.expanded_height = 5 * default_row_height_ + r.container->size().height();
+ r.expanded_height = 2 * default_row_height_ + r.container->sizeHint().height();
}
r.animation_step = 0;
r.anim_height = r.height;
- update_expanded_rows();
animation_timer_.start();
}
}
void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
{
+ // Set colors and fixed widths
QFontMetrics m(QApplication::font());
QPalette header_palette = owner_->view()->palette();
const int w = m.boundingRect(r->decode_row->title()).width() + RowTitleMargin;
r->title_width = w;
- r->container->resize(owner_->view()->viewport()->width() - r->container->pos().x(),
- r->expanded_height - 2 * default_row_height_);
- r->container->setVisible(false);
+ // Set up top-level container
+ connect(r->container, SIGNAL(widgetResized(QWidget*)),
+ this, SLOT(on_row_container_resized(QWidget*)));
QVBoxLayout* vlayout = new QVBoxLayout();
r->container->setLayout(vlayout);
// Add selector container
vlayout->addWidget(r->selector_container);
- r->selector_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- r->selector_container->setMinimumSize(0, 3 * default_row_height_); // FIXME
- r->selector_container->setLayout(new QHBoxLayout());
+ r->selector_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ r->selector_container->setLayout(new FlowLayout(r->selector_container));
r->selector_container->setAutoFillBackground(true);
r->selector_container->setPalette(selector_palette);
nr.expanded = false;
nr.collapsing = false;
nr.expand_marker_shape = default_marker_shape_;
- nr.container = new QWidget(owner_->view()->scrollarea());
+ nr.container = new ContainerWidget(owner_->view()->scrollarea());
nr.header_container = new QWidget(nr.container);
nr.selector_container = new QWidget(nr.container);
void DecodeTrace::update_expanded_rows()
{
for (DecodeTraceRow& r : rows_) {
- r.container->move(2 * ArrowSize,
- get_row_y(&r) + default_row_height_);
-
- r.container->resize(owner_->view()->viewport()->width() - r.container->pos().x(),
- r.height - 2 * default_row_height_);
+ if (r.expanding || r.expanded)
+ r.expanded_height = 2 * default_row_height_ + r.container->sizeHint().height();
+
+ if (r.expanded)
+ r.height = r.expanded_height;
+
+ int x = 2 * ArrowSize;
+ int y = get_row_y(&r) + default_row_height_;
+ // Only update the position if it actually changes
+ if ((x != r.container->pos().x()) || (y != r.container->pos().y()))
+ r.container->move(x, y);
+
+ int w = owner_->view()->viewport()->width() - x;
+ int h = r.height - 2 * default_row_height_;
+ // Only update the dimension if they actually change
+ if ((w != r.container->sizeHint().width()) || (h != r.container->sizeHint().height()))
+ r.container->resize(w, h);
}
}
owner_->row_item_appearance_changed(false, true);
}
+void DecodeTrace::on_row_container_resized(QWidget* sender)
+{
+ sender->update();
+
+ owner_->extents_changed(false, true);
+ owner_->row_item_appearance_changed(false, true);
+}
+
void DecodeTrace::on_copy_annotation_to_clipboard()
{
if (!selected_row_)
namespace views {
namespace trace {
+class ContainerWidget;
+
struct DecodeTraceRow {
// When adding a field, make sure it's initialized properly in
// DecodeTrace::update_rows()
QPolygon expand_marker_shape;
float anim_height, anim_shape;
- QWidget* container;
+ ContainerWidget* container;
QWidget* header_container;
QWidget* selector_container;
vector<QCheckBox*> selectors;
map<uint32_t, QColor> ann_class_color;
};
+class ContainerWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ContainerWidget(QWidget *parent = nullptr);
+
+ virtual void resizeEvent(QResizeEvent* event);
+
+Q_SIGNALS:
+ void widgetResized(QWidget* sender);
+};
+
class DecodeTrace : public Trace
{
Q_OBJECT
void on_show_hide_decoder(int index);
void on_show_hide_row(int row_id);
void on_show_hide_class(QWidget* sender);
+ void on_row_container_resized(QWidget* sender);
void on_copy_annotation_to_clipboard();
--- /dev/null
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 The Qt Company Ltd.
+ ** Contact: http://www.qt.io/licensing/
+ **
+ ** This file is part of the examples of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:BSD$
+ ** You may use this file under the terms of the BSD license as follows:
+ **
+ ** "Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are
+ ** met:
+ ** * Redistributions of source code must retain the above copyright
+ ** notice, this list of conditions and the following disclaimer.
+ ** * Redistributions in binary form must reproduce the above copyright
+ ** notice, this list of conditions and the following disclaimer in
+ ** the documentation and/or other materials provided with the
+ ** distribution.
+ ** * Neither the name of The Qt Company Ltd nor the names of its
+ ** contributors may be used to endorse or promote products derived
+ ** from this software without specific prior written permission.
+ **
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include <QWidget>
+
+#include "flowlayout.hpp"
+
+FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) :
+ QLayout(parent),
+ m_parent(parent),
+ m_hSpace(hSpacing),
+ m_vSpace(vSpacing)
+{
+ setContentsMargins(margin, margin, margin, margin);
+}
+
+FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) :
+ m_parent(nullptr),
+ m_hSpace(hSpacing),
+ m_vSpace(vSpacing)
+{
+ setContentsMargins(margin, margin, margin, margin);
+}
+
+FlowLayout::~FlowLayout()
+{
+ QLayoutItem *item;
+ while ((item = takeAt(0)))
+ delete item;
+}
+
+void FlowLayout::addItem(QLayoutItem *item)
+{
+ itemList.append(item);
+}
+
+int FlowLayout::horizontalSpacing() const
+{
+ if (m_hSpace >= 0)
+ return m_hSpace;
+ else
+ return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+}
+
+int FlowLayout::verticalSpacing() const
+{
+ if (m_vSpace >= 0)
+ return m_vSpace;
+ else
+ return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
+}
+
+int FlowLayout::count() const
+{
+ return itemList.size();
+}
+
+QLayoutItem *FlowLayout::itemAt(int index) const
+{
+ return itemList.value(index);
+}
+
+QLayoutItem *FlowLayout::takeAt(int index)
+{
+ if ((index >= 0) && (index < itemList.size()))
+ return itemList.takeAt(index);
+ else
+ return 0;
+}
+
+Qt::Orientations FlowLayout::expandingDirections() const
+{
+ return Qt::Horizontal | Qt::Vertical;
+}
+
+bool FlowLayout::hasHeightForWidth() const
+{
+ return true;
+}
+
+int FlowLayout::heightForWidth(int width) const
+{
+ int height = doLayout(QRect(0, 0, width, 0), true);
+ return height;
+}
+
+void FlowLayout::setGeometry(const QRect &rect)
+{
+ QLayout::setGeometry(rect);
+ doLayout(rect, false);
+}
+
+QSize FlowLayout::sizeHint() const
+{
+ return minimumSize();
+}
+
+QSize FlowLayout::minimumSize() const
+{
+ QSize size(0, 0);
+
+ for (QLayoutItem* item : itemList) {
+ int w = item->geometry().x() + item->geometry().width();
+ if (w > size.width())
+ size.setWidth(w);
+
+ int h = item->geometry().y() + item->geometry().width();
+ if (h > size.height())
+ size.setHeight(h);
+ }
+
+ size += QSize(2 * margin(), 2 * margin());
+
+ return size;
+}
+
+int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
+{
+ int left, top, right, bottom;
+ getContentsMargins(&left, &top, &right, &bottom);
+
+ QRect effectiveRect = rect.adjusted(left, top, -right, -bottom);
+ int x = effectiveRect.x();
+ int y = effectiveRect.y();
+
+ int lineHeight = 0;
+ for (QLayoutItem* item : itemList) {
+ QWidget* w = item->widget();
+
+ int spaceX = horizontalSpacing();
+ if (spaceX == -1)
+ spaceX = w->style()->layoutSpacing(
+ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
+
+ int spaceY = verticalSpacing();
+ if (spaceY == -1)
+ spaceY = w->style()->layoutSpacing(
+ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
+
+ int nextX = x + item->sizeHint().width() + spaceX;
+ if (((nextX - spaceX) > effectiveRect.right()) && (lineHeight > 0)) {
+ x = effectiveRect.x();
+ y = y + lineHeight + spaceY;
+ nextX = x + item->sizeHint().width() + spaceX;
+ lineHeight = 0;
+ }
+
+ if (!testOnly)
+ item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
+
+ x = nextX;
+ lineHeight = qMax(lineHeight, item->sizeHint().height());
+ }
+
+ int height = y + lineHeight - rect.y() + bottom;
+
+ if (m_parent)
+ m_parent->setMinimumHeight(height);
+
+ return height;
+}
+
+int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
+{
+ QObject *parent = this->parent();
+
+ if (!parent)
+ return -1;
+
+ if (parent->isWidgetType()) {
+ QWidget *pw = qobject_cast<QWidget*>(parent);
+ return pw->style()->pixelMetric(pm, 0, pw);
+ } else
+ return static_cast<QLayout*>(parent)->spacing();
+}
--- /dev/null
+/****************************************************************************
+ **
+ ** Copyright (C) 2015 The Qt Company Ltd.
+ ** Contact: http://www.qt.io/licensing/
+ **
+ ** This file is part of the examples of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:BSD$
+ ** You may use this file under the terms of the BSD license as follows:
+ **
+ ** "Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are
+ ** met:
+ ** * Redistributions of source code must retain the above copyright
+ ** notice, this list of conditions and the following disclaimer.
+ ** * Redistributions in binary form must reproduce the above copyright
+ ** notice, this list of conditions and the following disclaimer in
+ ** the documentation and/or other materials provided with the
+ ** distribution.
+ ** * Neither the name of The Qt Company Ltd nor the names of its
+ ** contributors may be used to endorse or promote products derived
+ ** from this software without specific prior written permission.
+ **
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#ifndef FLOWLAYOUT_H
+#define FLOWLAYOUT_H
+
+#include <QLayout>
+#include <QRect>
+#include <QStyle>
+#include <QWidgetItem>
+
+class FlowLayout : public QLayout
+{
+public:
+ FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
+ FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
+ ~FlowLayout();
+
+ void addItem(QLayoutItem *item);
+ int horizontalSpacing() const;
+ int verticalSpacing() const;
+ Qt::Orientations expandingDirections() const;
+ bool hasHeightForWidth() const;
+ int heightForWidth(int) const;
+ int count() const;
+ QLayoutItem *itemAt(int index) const;
+ QSize minimumSize() const;
+ void setGeometry(const QRect &rect);
+ QSize sizeHint() const;
+ QLayoutItem *takeAt(int index);
+
+private:
+ int doLayout(const QRect &rect, bool testOnly) const;
+ int smartSpacing(QStyle::PixelMetric pm) const;
+
+ QWidget* m_parent;
+ QList<QLayoutItem*> itemList;
+ int m_hSpace, m_vSpace;
+};
+
+#endif
${PROJECT_SOURCE_DIR}/pv/widgets/colorpopup.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/devicetoolbutton.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/exportmenu.cpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/flowlayout.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/importmenu.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/popup.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/popuptoolbutton.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/colorpopup.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/devicetoolbutton.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/exportmenu.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/flowlayout.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/importmenu.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/popup.hpp
${PROJECT_SOURCE_DIR}/pv/widgets/popuptoolbutton.hpp