OmaPhoto 1.0.0: the Linux port of Compositor
Release / deb (push) Canceled after 0s
Release / rpm (push) Canceled after 0s
Release / arch (push) Canceled after 0s
Release / publish (push) Canceled after 0s

A layered image editor for Linux and Omarchy, ported from Compositor
(github.com/robbietilton/Compositor), whose Swift source is its
specification. It reads and writes Compositor's .comp projects.

Packages for Ubuntu, Fedora and Arch come from the release workflow;
scripts/install/ holds one install script per distribution family,
and flake.nix serves NixOS.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
雷电芽衣andClaude committed 2026-09-26 00:44:52 -04:00
commit c864304695
564 files changed
+100804

No files matched your search

+95
View File
@@ -0,0 +1,95 @@
#include "UI/ShapeControls.h"
#include "Document/EditorSession.h"
#include "UI/ColorPaletteControls.h"
#include "UI/LassoControls.h"
#include <QButtonGroup>
#include <QHBoxLayout>
#include <QLabel>
#include <QSignalBlocker>
#include <QSlider>
#include <QToolButton>
#include <cmath>
ShapeControls::ShapeControls(EditorSession &session, QWidget *parent)
: ToolHeaderBar(QStringLiteral("Shape"), parent), m_session(session), m_kinds(new QButtonGroup(this)),
m_kindButtons{kind(ShapeKind::rectangle), kind(ShapeKind::ellipse), kind(ShapeKind::line)},
m_widthSlider(new QSlider(Qt::Horizontal, this)),
m_widthField(new SelectionAmountField(session, 1, 5000, [this] { return m_session.shapeLineWidth(); },
[this](double width) { m_session.setShapeLineWidth(width); }, this)),
m_width(amount(QStringLiteral("Width"), m_widthSlider, m_widthField)), m_radiusSlider(new QSlider(Qt::Horizontal, this)),
m_radiusField(new SelectionAmountField(session, 0, 5000, [this] { return m_session.shapeCornerRadius(); },
[this](double radius) { m_session.setShapeCornerRadius(radius); }, this)),
m_radius(amount(QStringLiteral("Radius"), m_radiusSlider, m_radiusField)),
m_fill(new SwatchButton([&session] { return session.foregroundColor(); }, 3, 0, 0.5, this))
{
m_widthSlider->setObjectName(QStringLiteral("shapeWidthSlider"));
m_widthSlider->setRange(1, 100);
connect(m_widthSlider, &QSlider::valueChanged, this, [this](int value) { m_session.setShapeLineWidth(value); });
m_widthField->setObjectName(QStringLiteral("shapeWidth"));
m_width->setObjectName(QStringLiteral("shapeWidthGroup"));
m_radiusSlider->setObjectName(QStringLiteral("shapeRadiusSlider"));
m_radiusSlider->setRange(0, 200);
connect(m_radiusSlider, &QSlider::valueChanged, this, [this](int value) { m_session.setShapeCornerRadius(value); });
m_radiusField->setObjectName(QStringLiteral("shapeRadius"));
m_radius->setObjectName(QStringLiteral("shapeRadiusGroup"));
m_radius->setToolTip(QStringLiteral("Round the rectangle's corners by this many pixels; 0 keeps them square"));
m_fill->setObjectName(QStringLiteral("shapeFill"));
m_fill->setFixedSize(36, 18);
m_fill->setToolTip(QStringLiteral("Shapes fill with the foreground color; click to change it"));
connect(m_fill, &QAbstractButton::clicked, this, [this] { m_session.openColorPicker(false); });
auto *fill = new QWidget(this);
auto *fillRow = new QHBoxLayout(fill);
fillRow->setContentsMargins(0, 0, 0, 0);
fillRow->setSpacing(6);
fillRow->addWidget(new QLabel(QStringLiteral("Fill"), fill));
fillRow->addWidget(m_fill);
for (QWidget *widget : std::initializer_list<QWidget *>{m_kindButtons[0], m_kindButtons[1], m_kindButtons[2], m_width, m_radius, fill})
row->insertWidget(row->count() - 1, widget);
connect(&m_session, &EditorSession::changed, this, &ShapeControls::synchronize);
synchronize();
}
// A segment of Swift's picker; choosing drops a draft first.
QToolButton *ShapeControls::kind(ShapeKind value)
{
auto *button = new QToolButton(this);
button->setObjectName(QStringLiteral("shape") + rawValue(value));
button->setText(rawValue(value));
button->setCheckable(true);
button->setToolTip(QStringLiteral("Shift-U (or Tab) steps through Rectangle, Ellipse and Line"));
m_kinds->addButton(button);
connect(button, &QToolButton::clicked, this, [this, value] {
m_session.cancelShape();
m_session.setShapeKind(value);
});
return button;
}
QWidget *ShapeControls::amount(const QString &name, QSlider *slider, SelectionAmountField *field)
{
auto *group = new QWidget(this);
auto *layout = new QHBoxLayout(group);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(6);
slider->setFixedWidth(100);
field->setFixedWidth(48);
for (QWidget *widget : std::initializer_list<QWidget *>{new QLabel(name, group), slider, field, new QLabel(QStringLiteral("px"), group)})
layout->addWidget(widget);
return group;
}
void ShapeControls::synchronize()
{
const ShapeKind kind = m_session.shapeKind();
m_kindButtons[size_t(kind)]->setChecked(true);
m_width->setVisible(kind == ShapeKind::line);
m_radius->setVisible(kind == ShapeKind::rectangle);
// The sliders show the session's number without writing back.
const QSignalBlocker widthBlocker(m_widthSlider), radiusBlocker(m_radiusSlider);
m_widthSlider->setValue(int(std::lround(std::min(100.0, m_session.shapeLineWidth()))));
m_radiusSlider->setValue(int(std::lround(std::min(200.0, m_session.shapeCornerRadius()))));
m_widthField->sync(int(std::lround(m_session.shapeLineWidth())));
m_radiusField->sync(int(std::lround(m_session.shapeCornerRadius())));
m_fill->update();
setEnabled(!m_session.showsBusy() && m_session.document().has_value());
}