Pith - whittle
#include "ui/SettingsDialog.h"
#include <QAbstractButton>
#include <QDialogButtonBox>
#include <QFontMetrics>
#include <QFormLayout>
#include <QFrame>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QList>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QPair>
#include <QSet>
#include <QSpacerItem>
#include <QVBoxLayout>
#include <QWidget>

namespace {
	constexpr char kHeadStyle[]  = "color: #999999; font-size: 8pt;";
	constexpr char kRuleStyle[]  = "color: #cccccc; background-color: #cccccc;";

	constexpr char kSheet[] =
	"QDialog { background: #F4F4F4; }"
	"QPushButton { border: 1px solid #c1c1c1; border-radius: 4px;"
	" padding: 4px 12px; background: #F9F9F9; color: #000000; }"
	"QPushButton:hover { background: #F1F1F1; }"
	"QPushButton:pressed { background: #F4F4F4; }";

	constexpr int kMetricSize = 1 << 0;
	constexpr int kMetricTime = 1 << 1;

	constexpr int kRowSpacing = 7;
	constexpr int kLabelGap   = 15;

	constexpr int kPillH   = 28;
	constexpr int kPillR   = 4;
	constexpr int kPillPad = 14;
	constexpr int kActionGap = 8;

	const QColor kPillBorder  {0xC1, 0xC1, 0xC1};
	const QColor kPillOff     {0xFA, 0xFA, 0xFA};
	const QColor kPillOn      {0xE6, 0xE6, 0xE6};
	const QColor kPillHover   {0xF1, 0xF1, 0xF1};
	const QColor kPillText    {0x33, 0x33, 0x33};
	const QColor kPillTextOn  {0x22, 0x22, 0x22};
	const QColor kPillOnDim   {0xEE, 0xEE, 0xEE};
	const QColor kPillTextDim {0xAA, 0xAA, 0xAA};

	QSpacerItem* spacer(int h) {
		return new QSpacerItem(0, h, QSizePolicy::Minimum, QSizePolicy::Fixed);
	}
}

namespace whittle {

	PillRow::PillRow(const QList<PillItem>& items, bool exclusive, QWidget* parent)
	: QWidget(parent), m_items(items), m_exclusive(exclusive) {
		setMouseTracking(true);
		setCursor(Qt::PointingHandCursor);
		const QFontMetrics fm(font());
		int x = 0;
		for (const PillItem& it : m_items) {
			m_x << x;
			x += fm.horizontalAdvance(it.first) + kPillPad * 2;
		}
		m_x << x;
		setFixedSize(x, kPillH);
	}

	void PillRow::check(qint64 v) {
		if (m_exclusive) m_checked.clear();
		m_checked.insert(v);
		update();
		emit changed();
	}

	bool PillRow::isChecked(qint64 v) const { return m_checked.contains(v); }
	int  PillRow::checkedCount() const { return int(m_checked.size()); }

	qint64 PillRow::checkedValue(qint64 fallback) const {
		for (const PillItem& it : m_items)
			if (m_checked.contains(it.second)) return it.second;
		return fallback;
	}

	int PillRow::hitTest(const QPoint& p) const {
		for (int i = 0; i < m_items.size(); ++i)
			if (p.x() >= m_x[i] && p.x() < m_x[i + 1]) return i;
		return -1;
	}

	void PillRow::mousePressEvent(QMouseEvent* e) {
		const int i = hitTest(e->pos());
		if (i < 0) return;
		const qint64 v = m_items[i].second;
		if (m_exclusive) {
			m_checked.clear();
			m_checked.insert(v);
		} else if (m_checked.contains(v)) {
if (m_checked.size() > 1) m_checked.remove(v);   // keep one
} else {
	m_checked.insert(v);
}
update();
emit changed();
}

void PillRow::mouseMoveEvent(QMouseEvent* e) {
	const int h = hitTest(e->pos());
	if (h != m_hover) { m_hover = h; update(); }
}

void PillRow::leaveEvent(QEvent*) {
	if (m_hover != -1) { m_hover = -1; update(); }
}

void PillRow::paintEvent(QPaintEvent*) {
	QPainter p(this);
	p.setRenderHint(QPainter::Antialiasing);
	const bool live = isEnabled();

	QPainterPath outline;
	outline.addRoundedRect(QRectF(0.5, 0.5, width() - 1, height() - 1),
		kPillR, kPillR);

	p.save();
	p.setClipPath(outline);
	p.fillRect(rect(), kPillOff);
	for (int i = 0; i < m_items.size(); ++i) {
		const bool on = m_checked.contains(m_items[i].second);
		const bool hover = live && i == m_hover;
		if (!on && !hover) continue;
		p.fillRect(QRect(m_x[i], 0, m_x[i + 1] - m_x[i], height()),
			on ? (live ? kPillOn : kPillOnDim) : kPillHover);
	}
	p.restore();

	p.setPen(QPen(kPillBorder, 1));
	for (int i = 1; i < m_items.size(); ++i)
		p.drawLine(QPointF(m_x[i] + 0.5, 0.5),
			QPointF(m_x[i] + 0.5, height() - 0.5));
	p.setBrush(Qt::NoBrush);
	p.drawPath(outline);

	for (int i = 0; i < m_items.size(); ++i) {
		const bool on = m_checked.contains(m_items[i].second);
		p.setPen(!live ? kPillTextDim : on ? kPillTextOn : kPillText);
		p.drawText(QRect(m_x[i], 0, m_x[i + 1] - m_x[i], height()),
			Qt::AlignCenter, m_items[i].first);
	}
}

SettingsDialog::SettingsDialog(const FilterSettings& initial, QWidget* parent)
: QDialog(parent) {
	setWindowTitle(tr("Settings"));
	setModal(true);
	setStyleSheet(QLatin1String(kSheet));

	m_metric = new PillRow({{tr("Size"),  kMetricSize},
{tr("mTime"), kMetricTime}}, /*exclusive=*/false);
		if (initial.compareSize) m_metric->check(kMetricSize);
		if (initial.compareTime) m_metric->check(kMetricTime);
		if (m_metric->checkedCount() == 0) {
			m_metric->check(kMetricSize);
			m_metric->check(kMetricTime);
		}

		m_tol = new PillRow({{tr("ext4"),   qint64(FilterSettings::Tol_ext4)},
			{tr("NTFS"), qint64(FilterSettings::Tol_ntfs)},
			{tr("ext2/3"),    qint64(FilterSettings::Tol_ext23)},
			{tr("VFAT"),    qint64(FilterSettings::Tol_vfat)}}, true);
		m_tol->check(qint64(initial.toleranceNs));
		if (m_tol->checkedCount() == 0)
			m_tol->check(qint64(FilterSettings::Tol_ext4));

		m_display = new PillRow({{tr("Only Diff"), int(FilterSettings::Display::OnlyDiff)},
			{tr("Show All"),  int(FilterSettings::Display::All)}}, true);
		m_display->check(int(initial.display));

		m_recursive = new PillRow({{tr("Non"), 0}, {tr("Recursive"), 1}}, true);
		m_recursive->check(initial.recursive ? 1 : 0);

		m_loadLast = new PillRow({{tr("Load Last"), 1}, {tr("Reset"), 0}}, true);
		m_loadLast->check(initial.loadLastScheme ? 1 : 0);

		auto* form = new QFormLayout;
		form->setVerticalSpacing(kRowSpacing);
		form->setHorizontalSpacing(kLabelGap);
		form->setContentsMargins(0, 0, 0, 0);
		form->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
		form->setRowWrapPolicy(QFormLayout::DontWrapRows);
		form->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
		form->addRow(tr("Metric"),    m_metric);
		form->addRow(tr("Tolerance"), m_tol);
		form->addRow(tr("Display"),   m_display);
		form->addItem(spacer(4));

		form->addRow(tr("Directories"), m_recursive);
		form->addRow(tr("Scheme"),      m_loadLast);
		form->addItem(spacer(6));

		auto* buttons = new QDialogButtonBox(
			QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
		for (QAbstractButton* b : buttons->buttons()) b->setIcon(QIcon());
			connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
		connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);

		auto* v = new QVBoxLayout(this);
		v->setContentsMargins(16, 14, 16, 14);
		v->setSpacing(0);
		v->setSizeConstraint(QLayout::SetFixedSize);
		v->addLayout(form);
		v->addSpacing(kActionGap);
		v->addWidget(buttons);
	}

	FilterSettings SettingsDialog::settings() const {
		FilterSettings f;
		f.compareSize = m_metric->isChecked(kMetricSize);
		f.compareTime = m_metric->isChecked(kMetricTime);
		if (!f.compareSize && !f.compareTime) f.compareSize = f.compareTime = true;
		f.toleranceNs = m_tol->checkedValue(qint64(FilterSettings::Tol_ext4));
		f.display = (m_display->checkedValue(int(FilterSettings::Display::OnlyDiff))
			== int(FilterSettings::Display::All))
		? FilterSettings::Display::All
		: FilterSettings::Display::OnlyDiff;
		f.recursive      = m_recursive->checkedValue(1) != 0;
		f.loadLastScheme = m_loadLast->checkedValue(0) != 0;
		return f;
	}

}