Add a Segmented Indicator

Just a POC of a new indicator style
This commit is contained in:
Sergio Martins
2020-08-23 13:20:37 +01:00
parent 35076bbdb6
commit 00b4dbc821
5 changed files with 236 additions and 4 deletions

View File

@@ -103,6 +103,7 @@ else()
private/widgets/TitleBarWidget.cpp
private/widgets/DockWidget.cpp
private/widgets/QWidgetAdapter_widgets.cpp
private/indicators/SegmentedIndicators.cpp
# private/indicators/AnimatedIndicators.cpp
)

View File

@@ -25,6 +25,7 @@
# include "widgets/TabWidgetWidget_p.h"
# include "multisplitter/Separator_qwidget.h"
# include "widgets/FloatingWindowWidget_p.h"
# include "indicators/SegmentedIndicators_p.h"
# include <QRubberBand>
#else
# include "quick/FrameQuick_p.h"

View File

@@ -31,7 +31,8 @@ public:
enum class Type {
None = 0,
Classic = 1,
Animated = 2
Segmented = 2,
// Animated = 3
};
Q_ENUM(Type)
@@ -45,7 +46,10 @@ public:
DropLocation_OutterLeft,
DropLocation_OutterTop,
DropLocation_OutterRight,
DropLocation_OutterBottom
DropLocation_OutterBottom,
DropLocation_First = DropLocation_Left,
DropLocation_Last = DropLocation_OutterBottom,
};
Q_ENUM(DropLocation)
@@ -61,7 +65,7 @@ public:
virtual Type indicatorType() const = 0;
void hover(QPoint globalPos);
virtual QPoint posForIndicator(DropLocation) const = 0; // Used by unit-tests only
virtual QPoint posForIndicator(DropLocation) const { return {}; }; // Used by unit-tests only
static KDDockWidgets::Location multisplitterLocationFor(DropLocation);
@@ -79,7 +83,7 @@ private:
protected:
virtual void hover_impl(QPoint globalPos) = 0;
virtual void onHoveredFrameChanged(Frame *);
virtual void updateVisibility() = 0;
virtual void updateVisibility() {};
Frame *m_hoveredFrame = nullptr;
QPointer<const FloatingWindow> m_windowBeingDragged;

View File

@@ -0,0 +1,179 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "SegmentedIndicators_p.h"
#include "DropArea_p.h"
#include <QPainter>
#include <QPainterPath>
#define SEGMENT_GIRTH 40
#define SEGMENT_PEN_WIDTH 4
using namespace KDDockWidgets;
SegmentedIndicators::SegmentedIndicators(DropArea *dropArea)
: DropIndicatorOverlayInterface(dropArea)
{
}
SegmentedIndicators::~SegmentedIndicators()
{
}
DropIndicatorOverlayInterface::Type SegmentedIndicators::indicatorType() const
{
return Type::Segmented;
}
void SegmentedIndicators::hover_impl(QPoint pt)
{
m_hoveredPt = mapFromGlobal(pt);
updateSegments();
setCurrentDropLocation(dropLocationForPos(m_hoveredPt));
}
DropIndicatorOverlayInterface::DropLocation SegmentedIndicators::dropLocationForPos(QPoint pos) const
{
for (auto it = m_segments.cbegin(), end = m_segments.cend(); it != end; ++it) {
if (it.value().containsPoint(pos, Qt::OddEvenFill)) {
return it.key();
}
}
return DropLocation_None;
}
void SegmentedIndicators::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing, true);
drawSegments(&p);
}
QVector<QPolygon> SegmentedIndicators::segmentsForRect(QRect r, QPolygon &center, bool useOffset) const
{
const int penWidth = SEGMENT_PEN_WIDTH;
const int halfPenWidth = penWidth / 2;
const int l = SEGMENT_GIRTH;
const int top = (r.y() == 0 && useOffset) ? l : r.y();
const int left = (r.x() == 0 && useOffset) ? l : r.x();
const int right = (rect().right() == r.right() && useOffset) ? r.right() - l : r.right();
const int bottom = (rect().bottom() == r.bottom() && useOffset) ? r.bottom() - l : r.bottom();
const QPoint topLeft = { left + halfPenWidth, top + halfPenWidth };
const QPoint topRight = { right, top + halfPenWidth };
const QPoint bottomLeft = { left + halfPenWidth, bottom };
const QPoint bottomRight = { right, bottom };
const QVector<QPoint> leftPoints = { topLeft, bottomLeft,
QPoint(left, bottom) + QPoint(l, -l),
topLeft + QPoint(l, l), topLeft
};
const QVector<QPoint> rightPoints = { topRight, bottomRight,
bottomRight + QPoint(-l, -l),
topRight + QPoint(-l, l)
};
const QVector<QPoint> topPoints = { topLeft, topRight,
topRight + QPoint(-l, l),
topLeft + QPoint(l, l)
};
const QVector<QPoint> bottomPoints = { bottomLeft, bottomRight,
bottomRight + QPoint(-l, -l),
bottomLeft + QPoint(l, -l)
};
{
QPolygon bounds = QVector<QPoint> { topLeft + QPoint(l, l),
topRight + QPoint(-l, l),
bottomRight + QPoint(-l, -l),
bottomLeft + QPoint(l, -l)
};
const int maxWidth = bounds.boundingRect().width();
const QPoint centerPos = bounds.boundingRect().center();
// Build the center
const int indicatorWidth = qMin(300, maxWidth - 100);
const int indicatorHeight = qMin(160, int(indicatorWidth * 0.60));
const int tabWidth = int(indicatorWidth * 0.267);
const int tabHeight = int(indicatorHeight * 0.187);
const int centerRectLeft = centerPos.x() - indicatorWidth / 2;
const int centerRectRight = centerPos.x() + indicatorWidth / 2;
const int centerRectBottom = centerPos.y() + indicatorHeight / 2;
const int centerRectTop = centerPos.y() - indicatorHeight / 2;
center = QVector<QPoint> { { centerRectLeft, centerRectTop },
{ centerRectLeft + tabWidth, centerRectTop },
{ centerRectLeft + tabWidth, centerRectTop + tabHeight },
{ centerRectRight, centerRectTop + tabHeight },
{ centerRectRight, centerRectBottom },
{ centerRectLeft, centerRectBottom },
};
}
return { leftPoints, topPoints, rightPoints, bottomPoints };
}
void SegmentedIndicators::updateSegments()
{
m_segments.clear();
const bool hasMultipleFrames = m_dropArea->count() > 1;
const bool needsInnerIndicators = hoveredFrameRect().isValid();
const bool needsOutterIndicators = hasMultipleFrames || !needsInnerIndicators;
QPolygon center;
if (needsInnerIndicators) {
const bool useOffset = needsOutterIndicators;
auto segments = segmentsForRect(hoveredFrameRect(), /*by-ref*/center, useOffset);
for (int i = 0; i < 4; ++i)
m_segments.insert(DropLocation(DropLocation_Left + i), segments[i]);
m_segments.insert(DropLocation_Center, center);
}
if (needsOutterIndicators) {
auto segments = segmentsForRect(rect(), /*unused*/center);
for (int i = 0; i < 4; ++i)
m_segments.insert(DropLocation(DropLocation_OutterLeft + i), segments[i]);
}
update();
}
void SegmentedIndicators::drawSegments(QPainter *p)
{
for (int i = DropLocation_First; i <= DropLocation_Last; ++i)
drawSegment(p, m_segments.value(DropLocation(i)));
}
void SegmentedIndicators::drawSegment(QPainter *p, const QPolygon &segment)
{
if (segment.isEmpty())
return;
QPen pen(Qt::black);
pen.setWidth(SEGMENT_PEN_WIDTH);
p->setPen(pen);
QColor brush = QColor(0, 0xFF, 0xFF, 128);
if (segment.containsPoint(m_hoveredPt, Qt::OddEvenFill))
brush = brush.darker(200);
p->setBrush(brush);
p->drawPolygon(segment);
}

View File

@@ -0,0 +1,47 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef KD_SEGMENTED_INDICATORS_P_H
#define KD_SEGMENTED_INDICATORS_P_H
#include "DropIndicatorOverlayInterface_p.h"
#include <QHash>
#include <QPolygon>
namespace KDDockWidgets {
class SegmentedIndicators : public DropIndicatorOverlayInterface
{
Q_OBJECT
public:
explicit SegmentedIndicators(DropArea *dropArea);
~SegmentedIndicators() override;
Type indicatorType() const override;
void hover_impl(QPoint globalPos) override;
DropLocation dropLocationForPos(QPoint pos) const;
protected:
void paintEvent(QPaintEvent *) override;
private:
QVector<QPolygon> segmentsForRect(QRect, QPolygon &center, bool useOffset = false) const;
void updateSegments();
void drawSegments(QPainter *p);
void drawSegment(QPainter *p, const QPolygon &segment);
QPoint m_hoveredPt = {};
QHash<DropLocation, QPolygon> m_segments;
};
}
#endif