Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions MetaUI/qt/include/meta_qt/designs/industrial/editor_style.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* Copyright (c) 2026 Otto Link. Distributed under the terms of the GNU General
Public License. The full license is in the file LICENSE, distributed with
this software. */
#pragma once
#include "meta_qt/ui/theme.hpp"
#include <QAbstractButton>
#include <QLabel>
#include <QLayout>
#include <QPalette>
namespace meta::qt::industrial
{
inline void style_editor(QWidget *host, const Theme &theme)
{
host->setObjectName("IndustrialEditor");
QPalette palette = host->palette();
palette.setColor(QPalette::Base, theme.bar);
palette.setColor(QPalette::Window, theme.section_surface);
palette.setColor(QPalette::Text, theme.ink_primary);
palette.setColor(QPalette::WindowText, theme.ink_primary);
palette.setColor(QPalette::ButtonText, theme.ink_primary);
palette.setColor(QPalette::Button, theme.field);
palette.setColor(QPalette::Mid, theme.field_border);
palette.setColor(QPalette::Dark, theme.hairline);
palette.setColor(QPalette::Light, theme.thumb_top);
palette.setColor(QPalette::Highlight, theme.accent);
palette.setColor(QPalette::PlaceholderText, theme.ink_secondary);
for (auto role : {QPalette::Text, QPalette::WindowText, QPalette::ButtonText})
palette.setColor(QPalette::Disabled, role, theme.ink_dim);
host->setPalette(palette);
host->setFont(row_label_font());
host->setStyleSheet(
QString(
"#IndustrialEditor QLabel { color: %1; background: transparent; }"
"#IndustrialEditor QPushButton, #IndustrialEditor QToolButton, "
"#IndustrialEditor QComboBox {"
" color: %1; background: %2; border: 1px solid %3; border-radius: 5px; "
"padding: 3px 7px; }"
"#IndustrialEditor QPushButton:hover, #IndustrialEditor QToolButton:hover { "
"border-color: %4; }"
"#IndustrialEditor QPushButton:checked { background: %4; color: %1; }"
"#IndustrialEditor QPushButton:disabled, #IndustrialEditor "
"QToolButton:disabled { color: %5; background: transparent; border-color: %3; }"
"#IndustrialEditor QScrollArea { border: none; background: transparent; }"
"#IndustrialEditor QScrollBar:vertical { width: 8px; background: transparent; }"
"#IndustrialEditor QScrollBar::handle:vertical { background: %3; "
"border-radius: 3px; min-height: 24px; }"
"#IndustrialEditor QScrollBar::add-line:vertical, #IndustrialEditor "
"QScrollBar::sub-line:vertical { height: 0px; }"
"#IndustrialEditor QScrollBar::add-page:vertical, #IndustrialEditor "
"QScrollBar::sub-page:vertical { background: transparent; }"
"#IndustrialEditor QComboBox::drop-down { border: none; width: 20px; }"
"#IndustrialEditor QToolButton::menu-indicator { image: none; }"
"#IndustrialEditor QPushButton[preset_name] { padding: 2px; border: 2px solid "
"transparent; border-radius: 5px; background: transparent; }"
"#IndustrialEditor QPushButton[preset_name]:hover { border-color: %5; }"
"#IndustrialEditor QPushButton[preset_name]:checked { border-color: %4; "
"background: transparent; }")
.arg(theme.ink_primary.name(),
theme.field.name(),
theme.field_border.name(),
theme.accent.name(),
theme.ink_dim.name()));
if (host->layout())
host->layout()->setSpacing(8);
for (auto *child : host->findChildren<QWidget *>())
{
child->setProperty("industrialEditor", true);
child->setPalette(palette);
}
for (auto *label : host->findChildren<QLabel *>())
{
label->setFont(row_label_font());
auto text = label->text();
if (!text.isEmpty())
{
text[0] = text[0].toUpper();
label->setText(text);
}
}
for (auto *button : host->findChildren<QAbstractButton *>())
{
if (button->property("preset_name").isValid())
continue;
button->setFont(ui_font(12));
button->setFixedHeight(28);
button->setCursor(Qt::PointingHandCursor);
}
}
} // namespace meta::qt::industrial
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class IntSlider : public Control<int>

int min_ = 0;
int max_ = 1;
int input_max_ = 1;
int value_ = 0;
std::string label_;
std::string category_;
Expand Down
37 changes: 37 additions & 0 deletions MetaUI/qt/include/meta_qt/designs/industrial/linked_sliders.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* Copyright (c) 2026 Otto Link. Distributed under the terms of the GNU General
Public License. The full license is in the file LICENSE, distributed with
this software. */
#pragma once
#ifdef META_ENABLE_GLM_TYPES
#include "meta_common.hpp"
#include "meta_qt/designs/industrial/param_slider.hpp"
#include "meta_qt/ui/control.hpp"
#include <glm/glm.hpp>
class QToolButton;
namespace meta::qt::industrial
{
// Shared scalar controls give bounded and unbounded pairs identical behavior.
class LinkedSliders : public Control<glm::vec2>
{
Q_OBJECT
public:
LinkedSliders(Attribute<glm::vec2> &, const RowContext &, QWidget * = nullptr);
static bool can_render(const Attribute<glm::vec2> &) { return true; }
glm::vec2 get() const override { return value_; }
void set(const glm::vec2 &) override;
QSize sizeHint() const override;

protected:
void on_state_changed() override;

private:
Theme axes_theme_;
Attribute<float> axes_[2] = {{"x", 0.f}, {"y", 0.f}};
ParamSlider *sliders_[2] = {nullptr, nullptr};
QToolButton *link_ = nullptr;
AttributeContainer *state_ = nullptr;
glm::vec2 value_{};
bool linked_ = false;
};
} // namespace meta::qt::industrial
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class ParamSlider : public Control<float>

float min_ = 0.f;
float max_ = 1.f;
float input_max_ = 1.f;
float value_ = 0.f;
bool log_scale_ = false;
int decimals_ = 2;
Expand Down
74 changes: 74 additions & 0 deletions MetaUI/qt/include/meta_qt/designs/industrial/text_row.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* Copyright (c) 2026 Otto Link. Distributed under the terms of the GNU General
Public License. The full license is in the file LICENSE, distributed with
this software. */
#pragma once
#include <string>

#include "meta_common.hpp"

#include "meta_qt/ui/control.hpp"

class QLineEdit;

namespace meta::qt::industrial
{

/** @brief Label and a text field, for a std::string attribute.
*
* Covers both SingleLineText and ReadOnlyText, which differ only in whether
* the field accepts typing. 20 rows in a Hesiod panel.
*
* The field stretches from the label column to the right edge rather than
* using the sliders' narrow value box. A slider's readout is a number a few
* characters wide sitting beside a rail that needs the room; a text row has no
* rail and its content is the whole point, so giving it the same 74px box
* would truncate almost everything worth typing.
*
* Chrome comes from field_stylesheet, the same function the slider readouts
* use, so the two kinds of field are the same object at different widths.
*/
class TextRow : public Control<std::string>
{
Q_OBJECT

public:
TextRow(Attribute<std::string> &attr, const RowContext &ctx, QWidget *parent = nullptr);

/// Any string attribute. There is no metadata this row cannot honour.
static bool can_render(const Attribute<std::string> &attr);

std::string get() const override { return value_; }
void set(const std::string &value) override;

QSize sizeHint() const override;

protected:
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void on_state_changed() override;
bool eventFilter(QObject *watched, QEvent *event) override;

private:
/// Label column, matching the sliders so the two line up down the panel.
QRect label_rect() const;

/// Everything to the right of the label.
QRect field_rect() const;

void refresh_field();
void restyle_field(bool editing = false);

std::string value_;
std::string label_;

/** @brief Declared read only by the attribute, as opposed to locked.
*
* Separate from is_locked(), which is a runtime state the host drives. A
* ReadOnlyText is never editable whatever the host says.
*/
bool read_only_ = false;

QLineEdit *field_ = nullptr;
};

} // namespace meta::qt::industrial
27 changes: 27 additions & 0 deletions MetaUI/qt/include/meta_qt/ui/number_format.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* Copyright (c) 2026 Otto Link. Distributed under the terms of the GNU General
Public License. The full license is in the file LICENSE, distributed with
this software. */
#pragma once
#include <QString>
#include <algorithm>
#include <cmath>
namespace meta::qt
{
// Ordinary values stay compact; small nonzero values must never read as zero.
inline QString display_float(float value, int decimals = 2)
{
const double magnitude = std::abs(double(value));
if (!std::isfinite(value))
return QString::number(value);
decimals = std::clamp(decimals, 0, 8);
if (magnitude == 0 || magnitude >= std::pow(10., -decimals))
return QString::number(value == 0 ? 0 : value, 'f', decimals);
if (magnitude < 1e-10)
return QString::number(value, 'g', 6);
const int precision = std::min(12, int(std::ceil(-std::log10(magnitude))) + 5);
QString text = QString::number(value, 'f', precision);
while (text.endsWith('0') && text.size() - text.indexOf('.') - 1 > decimals)
text.chop(1);
return text;
}
} // namespace meta::qt
4 changes: 4 additions & 0 deletions MetaUI/qt/include/meta_qt/widgets/array_canvas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class ArrayCanvas : public QWidget
QWidget *parent = nullptr);

QSize sizeHint() const override;
QSize minimumSizeHint() const override { return QSize(120, 120); }
int heightForWidth(int width) const override { return width; }

void set_field_data(const std::vector<float> &data);
const std::vector<float> &get_field_data() const;
Expand All @@ -40,6 +42,7 @@ class ArrayCanvas : public QWidget
void edit_ended();

protected:
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
Expand All @@ -51,6 +54,7 @@ class ArrayCanvas : public QWidget

private:
void draw_at(const QPoint &pos, Qt::MouseButtons buttons);
QPoint field_position(const QPoint &pos) const;
void update_geometry();
bool is_mouse_cursor_on_img() const;
QColor colormap(float v) const;
Expand Down
6 changes: 3 additions & 3 deletions MetaUI/qt/include/meta_qt/widgets/gradient_picker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ class GradientPicker : public QWidget
PresetGridWidget *preset_grid_ = nullptr;
bool rebuild_pending_ = false;

static constexpr int SWATCH_W = 60; // each preset swatch width
static constexpr int SWATCH_H = 32; // each preset swatch height
static constexpr int TOOLBAR_H = 24; // toolbar row height
static constexpr int SWATCH_W = 72; // each preset swatch width
static constexpr int SWATCH_H = 36; // each preset swatch height
static constexpr int TOOLBAR_H = 28; // toolbar row height

// Declared last so it disconnects before the members its callback touches
// are destroyed.
Expand Down
10 changes: 6 additions & 4 deletions MetaUI/qt/include/meta_qt/widgets/points_canvas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class PointsCanvas : public QWidget
QWidget *parent = nullptr);

void clear_all();
QSize sizeHint() const override { return QSize(320, 320); }
QSize minimumSizeHint() const override { return QSize(120, 120); }
int heightForWidth(int width) const override { return width; }
void randomize(int count);
void load_csv(const QString &path); // x,y,z per line (z clamped to [0,1])
void set_points(const std::vector<glm::vec3> &new_points);
Expand All @@ -67,10 +70,8 @@ class PointsCanvas : public QWidget
void drag_ended();

protected:
/// Keeps the canvas square: the point domain is square, so a fixed height
/// only matches it at one panel width.
void keyPressEvent(QKeyEvent *event) override;
void resizeEvent(QResizeEvent *event) override;

void paintEvent(QPaintEvent *) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
Expand All @@ -93,6 +94,7 @@ class PointsCanvas : public QWidget
float min_x_, max_x_, min_y_, max_y_, z_step_;

int hovered_idx_ = -1;
QString order_input_;
int drag_idx_ = -1;
bool moved_during_drag_ = false;
int hovered_segment_ = -1;
Expand All @@ -108,4 +110,4 @@ class PointsCanvas : public QWidget
static constexpr float POINT_R = 5.f;
};

} // namespace meta::qt
} // namespace meta::qt
6 changes: 5 additions & 1 deletion MetaUI/qt/include/meta_qt/widgets/range_bar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <QWidget>
#include <glm/glm.hpp>
#include "meta_qt/ui/theme.hpp"

namespace meta::qt
{
Expand All @@ -33,6 +34,7 @@ class RangeBar : public QWidget
QWidget *parent = nullptr);

void set_value(glm::vec2 v);
void set_theme(const Theme &theme) { theme_ = theme; industrial_ = true; update(); }
void set_histogram(const std::vector<float> &x, const std::vector<float> &y);

Q_SIGNALS:
Expand All @@ -47,6 +49,8 @@ class RangeBar : public QWidget
void leaveEvent(QEvent *) override;

private:
Theme theme_;
bool industrial_ = false;
enum class Handle
{
None,
Expand Down Expand Up @@ -82,4 +86,4 @@ class RangeBar : public QWidget
static constexpr int track_h_ = 8; // track height
};

} // namespace meta::qt
} // namespace meta::qt
6 changes: 6 additions & 0 deletions MetaUI/qt/src/designs/industrial/check_row.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ CheckRow::CheckRow(Attribute<bool> &attr,
{
key_ = attr.name();
label_ = meta::common::label(attr);
if (label_.find('_') != std::string::npos)
{
QString readable = QString::fromStdString(label_).replace('_', ' ');
if (!readable.isEmpty()) readable[0] = readable[0].toUpper();
label_ = readable.toStdString();
}
value_ = attr.value();
knob_ = value_ ? 1.0 : 0.0;

Expand Down
Loading