Pith - whittle
whittle/src/ui/SettingsDialog.cpp [8.8 kb]
Modified: 20:17:02 158 026 (24 Aug 026)
5 Days Ago
#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 kVersion[]    = "155.026.1";
constexpr char kSourceText[] = "vgmlr.com/mlwrk";
constexpr char kSourceUrl[]  = "https://vgmlr.com/mlwrk";
constexpr char kLinkStyle[]  = "text-decoration:none; color:#000000;"
                              " font-family:monospace; font-size:8pt;";
constexpr char kMetaStyle[]  = "font-family: monospace; font-size: 8pt;"
                               " padding-top: 2px; padding-bottom: 2px;";
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};

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

QLabel* metaLabel(const QString& text) {
    auto* l = new QLabel(text);
    l->setStyleSheet(QLatin1String(kMetaStyle));
    return l;
}
}

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();
}

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();
}

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);

    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);
        if (!on && i != m_hover) continue;
        p.fillRect(QRect(m_x[i], 0, m_x[i + 1] - m_x[i], height()),
                   on ? kPillOn : 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) {
        p.setPen(m_checked.contains(m_items[i].second) ? 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* version = metaLabel(QLatin1String(kVersion));
    version->setTextInteractionFlags(Qt::TextSelectableByMouse);

    auto* source = new QLabel(
        QStringLiteral("<a href=\"%1\" style=\"%2\">%3</a>")
            .arg(QLatin1String(kSourceUrl), QLatin1String(kLinkStyle),
                 QLatin1String(kSourceText)));
    source->setTextFormat(Qt::RichText);
    source->setOpenExternalLinks(true);
    source->setTextInteractionFlags(Qt::TextBrowserInteraction);

    source->setStyleSheet(QStringLiteral(
        "padding-top: 2px; padding-bottom: 2px;"));

    auto* form = new QFormLayout;
    form->setVerticalSpacing(kRowSpacing);
    form->setHorizontalSpacing(kLabelGap);
    form->setContentsMargins(0, 0, 0, 0);
    form->setLabelAlignment(Qt::AlignLeft | 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(4));

    form->addRow(tr("Version"),  version);
    form->addRow(tr("Source"), source);
    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;
}

}
Updates
Stave - Android 158.026
Kerf - Android 157.026
Kiln - Android 157.026
Wedge - Android 156.026
Whittle - Linux 155.026

Menu
Calendar
Project Tin (024/029)
Miter
RSS Feed
User Avatar
@vgmlr
=SUM(parts)
0.00183
260,112 (+526)