Fluent-Qt

Spatial 透视视图

SpatialView 空间视图

让现有控件以透视方式显示,并随鼠标倾斜。

示例

空间视图的真实 Gallery 示例

用法

调整场景透视

调整镜头角度和缩放,再把鼠标移到卡片上。需要输入文字或弹出窗口时,请切换到平面模式。

#include <FluentQt/Spatial.h>
using namespace fluent::spatial;

using namespace fluent;
// The two cards keep their own poses while the view changes the whole scene.
class SceneView final : public spatial::SpatialView {
public:
    using SpatialView::SpatialView;
    void arrange()
    {
        if (itemCount() != 2)
            return;
        const float spread = width() < 650 ? 24 : 100;
        items().first()->setPosition(QVector3D(-spread, -44, -160));
        items().last()->setPosition(QVector3D(spread, 44, 160));
    }
protected:
    void resizeEvent(QResizeEvent* event) override
    {
        SpatialView::resizeEvent(event);
        arrange();
    }
};
auto* panel = new SpatialPreviewPanel(parent);
auto* column = new QVBoxLayout(panel);
column->setContentsMargins(0, 0, 0, 0);
column->setSpacing(16);
auto* session = new spatial::SpatialSession(panel, panel);
const bool active = session->activate();
auto* view = new SceneView(panel);
if (!active || !session->attachView(view)) {
    view->setSpatialEnabled(false);
    view->setRenderMode(spatial::SpatialView::RenderMode::Raster);
}
view->setObjectName("spatialPreviewView");
view->setFixedHeight(400);
view->setCameraDistance(1200);
view->setZoom(0.9);
view->setMaximumTilt(QPointF(6, 12));
// Response time (140 ms), animation limit (60 fps), cache and Auto renderer use UILib defaults.
// See SpatialView.h for the full parameter contract.

// Both layers use the same size and data, making perspective easy to compare.
const auto makeLayer = [](const QString& title, const QString& caption) {
    auto* card = new layout::Card;
    card->setFixedSize(220, 150);
    auto* content = new QVBoxLayout(card);
    content->setContentsMargins(16, 12, 16, 12);
    content->setSpacing(6);
    auto* heading = new textfields::Label(title, card);
    heading->setFluentTypography(Typography::FontRole::Subtitle);
    heading->setTextColorRole(textfields::Label::TextColorRole::Primary);
    content->addWidget(heading);
    auto* chart = new charts::Sparkline(card);
    chart->setAccessibleName(title + " sample chart");
    auto* model = new charts::ChartModel(card);
    model->setPoints({{0, 12}, {1, 24}, {2, 18}, {3, 38}, {4, 32}, {5, 54}});
    chart->setModel(model);
    content->addWidget(chart, 1);
    auto* note = new textfields::Label(caption, card);
    note->setTextColorRole(textfields::Label::TextColorRole::Secondary);
    content->addWidget(note);
    return card;
};
spatial::SpatialItem* back = view->addWidget(
    makeLayer("Back layer", "Z -160 · sample data"), WidgetOwnership::Owned);
spatial::SpatialItem* front = view->addWidget(
    makeLayer("Front layer", "Z +160 · sample data"), WidgetOwnership::Owned);
back->setRotation(QVector3D(6, 16, 0));
front->setRotation(QVector3D(-6, -16, 0));
back->setSurfaceIntensity(0.75);
front->setSurfaceIntensity(0.75);
view->arrange();

auto* toolbar = new QHBoxLayout;
auto* follow = new basicinput::ToggleSwitch(panel);
follow->setObjectName("spatialViewFollow");
follow->setAccessibleName("Follow pointer");
follow->setOnContent("Follow pointer");
follow->setOffContent("Follow pointer");
follow->setIsOn(true);
toolbar->addWidget(follow);
toolbar->addStretch();
column->addLayout(toolbar);
column->addWidget(view);

// Keep view-wide controls outside the projection so their drag targets stay still.
auto* controls = new QWidget(panel);
controls->setObjectName("spatialViewControls");
auto* grid = new QGridLayout(controls);
grid->setContentsMargins(0, 0, 0, 0);
grid->setHorizontalSpacing(24);
grid->setVerticalSpacing(12);
grid->setColumnStretch(0, 1);
grid->setColumnStretch(1, 1);
const auto addSlider = [controls, grid](const QString& name, const QString& label,
                                      const QString& unit, int minimum, int maximum,
                                      int initial, int row, int col) {
    auto* group = new QWidget(controls);
    auto* layout = new QVBoxLayout(group);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(4);
    auto* text = new textfields::Label(group);
    text->setTextColorRole(textfields::Label::TextColorRole::Primary);
    auto* slider = new basicinput::Slider(group);
    slider->setObjectName(name);
    slider->setAccessibleName(label);
    slider->setRange(minimum, maximum);
    slider->setValue(initial);
    const auto updateText = [text, label, unit](int value) {
        text->setText(QString("%1: %2%3").arg(label).arg(value).arg(unit));
    };
    updateText(initial);
    QObject::connect(slider, &basicinput::Slider::valueChanged, text, updateText);
    layout->addWidget(text);
    layout->addWidget(slider);
    grid->addWidget(group, row, col);
    return slider;
};
auto* distance = addSlider("spatialViewDistance", "Camera distance", " px",
                           650, 2400, 1200, 0, 0);
auto* zoom = addSlider("spatialViewZoom", "Scene zoom", "%", 50, 110, 90, 0, 1);
QObject::connect(distance, &basicinput::Slider::valueChanged, view,
                 [view](int value) { view->setCameraDistance(value); });
QObject::connect(zoom, &basicinput::Slider::valueChanged, view,
                 [view](int value) { view->setZoom(value / 100.0); });
column->addWidget(controls);

QObject::connect(follow, &basicinput::ToggleSwitch::toggled,
                 view, &spatial::SpatialView::setPointerTrackingEnabled);
QObject::connect(view, &spatial::SpatialView::pointerTrackingEnabledChanged,
                 follow, &basicinput::ToggleSwitch::setIsOn);
const auto updateAvailability = [view, controls, follow] {
    controls->setEnabled(view->isSpatialEnabled());
    follow->setEnabled(view->isSpatialEnabled());
};
QObject::connect(view, &spatial::SpatialView::spatialEnabledChanged, controls, updateAvailability);
updateAvailability();

fluent::spatial::SpatialView

继承自 QWidget · FluentElement · QMLPlus

使用 OpenGL 承载少量具有透视变换的真实控件。

构造函数

属性

bool spatialEnabled

是否启用透视;动效和主题策略允许时默认 true。 关闭后将同一批控件放回原生纵向布局,保留原有状态。

读取 isSpatialEnabled()设置 setSpatialEnabled()通知 spatialEnabledChanged()
qreal cameraDistance

透视距离,逻辑像素范围 [100, 10000],默认 1000;越小,近大远小越明显。

读取 cameraDistance()设置 setCameraDistance()通知 cameraDistanceChanged()
qreal zoom

整个场景的显示倍率 [0.1, 4],默认 1;不改变条目的位置和姿态。

读取 zoom()设置 setZoom()通知 zoomChanged()
bool pointerTrackingEnabled

是否让整个场景随鼠标倾斜,默认 true,仅在 3D 中生效。

读取 isPointerTrackingEnabled()设置 setPointerTrackingEnabled()通知 pointerTrackingEnabledChanged()
QPointF maximumTilt

鼠标跟随的俯仰/偏航上限,各为 [0, 45] 度,默认 (10, 16)。

读取 maximumTilt()设置 setMaximumTilt()通知 maximumTiltChanged()
int responseTime

指针跟随时间常数 [0, 1000] 毫秒,默认 140;越大越柔和,0 表示立即跟随。 此值是指数响应的时间常数,不是固定动画时长。

读取 responseTime()设置 setResponseTime()通知 responseTimeChanged()
int maximumFrameRate

鼠标动画更新上限 [15, 120] 帧/秒,默认 60,不代表显示器实际帧率。

读取 maximumFrameRate()设置 setMaximumFrameRate()通知 maximumFrameRateChanged()
bool cacheEnabled

是否缓存控件表面,默认 true;内容发生变化时仍会重绘。

读取 isCacheEnabled()设置 setCacheEnabled()通知 cacheEnabledChanged()
RenderMode renderMode

请求的后端:Auto(默认)、Raster 或 OpenGL;OpenGL 不可用时回退。 切换等待当前鼠标操作结束;使用 activeBackend 查询实际后端。隐藏或完全裁出时释放 GPU 视口;祖先绘制特效启用时使用 Raster 供外层捕获,保留请求的渲染模式。

读取 renderMode()设置 setRenderMode()通知 renderModeChanged()
Backend activeBackend

实际渲染后端 Raster 或 OpenGL;只读,在视图显示并完成初始化前为 Raster。

读取 activeBackend()通知 rendererChanged()
QString rendererName

当前 OpenGL 设备名称;只读,Raster 或尚未初始化时为空。

读取 rendererName()通知 rendererChanged()
QString fallbackReason

自动回退后端的原因;只读,没有回退原因时为空。

读取 fallbackReason()通知 rendererChanged()
int itemCount

承载的表面数量,包含隐藏条目;只读,初始为 0。 外部销毁控件时立即移除条目,销毁完成后合并通知,允许回调销毁宿主。

读取 itemCount()通知 itemCountChanged()

方法

SpatialItem* addWidget(QWidget* widget, WidgetOwnership ownership = WidgetOwnership::Borrowed)

承载可支持的控件;拒绝空指针、不支持的控件或已在其他代理中的内容。

QWidget* takeWidget(SpatialItem* item)

分离内容并删除条目,将控件所有权转交调用方。

void releaseItem(SpatialItem* item)

删除条目并按记录的所有权策略释放控件。

int itemCount() const

承载的表面数量,包含隐藏条目;只读,初始为 0。 外部销毁控件时立即移除条目,销毁完成后合并通知,允许回调销毁宿主。

bool isSpatialEnabled() const

是否启用透视;动效和主题策略允许时默认 true。 关闭后将同一批控件放回原生纵向布局,保留原有状态。

void setSpatialEnabled(bool enabled)

是否启用透视;动效和主题策略允许时默认 true。 关闭后将同一批控件放回原生纵向布局,保留原有状态。

qreal cameraDistance() const

透视距离,逻辑像素范围 [100, 10000],默认 1000;越小,近大远小越明显。

void setCameraDistance(qreal distance)

透视距离,逻辑像素范围 [100, 10000],默认 1000;越小,近大远小越明显。

qreal zoom() const

整个场景的显示倍率 [0.1, 4],默认 1;不改变条目的位置和姿态。

void setZoom(qreal zoom)

整个场景的显示倍率 [0.1, 4],默认 1;不改变条目的位置和姿态。

bool isPointerTrackingEnabled() const

是否让整个场景随鼠标倾斜,默认 true,仅在 3D 中生效。

void setPointerTrackingEnabled(bool enabled)

是否让整个场景随鼠标倾斜,默认 true,仅在 3D 中生效。

QPointF maximumTilt() const

鼠标跟随的俯仰/偏航上限,各为 [0, 45] 度,默认 (10, 16)。

void setMaximumTilt(const QPointF& degrees)

鼠标跟随的俯仰/偏航上限,各为 [0, 45] 度,默认 (10, 16)。

int responseTime() const

指针跟随时间常数 [0, 1000] 毫秒,默认 140;越大越柔和,0 表示立即跟随。 此值是指数响应的时间常数,不是固定动画时长。

void setResponseTime(int milliseconds)

指针跟随时间常数 [0, 1000] 毫秒,默认 140;越大越柔和,0 表示立即跟随。 此值是指数响应的时间常数,不是固定动画时长。

int maximumFrameRate() const

鼠标动画更新上限 [15, 120] 帧/秒,默认 60,不代表显示器实际帧率。

void setMaximumFrameRate(int framesPerSecond)

鼠标动画更新上限 [15, 120] 帧/秒,默认 60,不代表显示器实际帧率。

bool isCacheEnabled() const

是否缓存控件表面,默认 true;内容发生变化时仍会重绘。

void setCacheEnabled(bool enabled)

是否缓存控件表面,默认 true;内容发生变化时仍会重绘。

RenderMode renderMode() const

请求的后端:Auto(默认)、Raster 或 OpenGL;OpenGL 不可用时回退。 切换等待当前鼠标操作结束;使用 activeBackend 查询实际后端。隐藏或完全裁出时释放 GPU 视口;祖先绘制特效启用时使用 Raster 供外层捕获,保留请求的渲染模式。

void setRenderMode(RenderMode mode)

请求的后端:Auto(默认)、Raster 或 OpenGL;OpenGL 不可用时回退。 切换等待当前鼠标操作结束;使用 activeBackend 查询实际后端。隐藏或完全裁出时释放 GPU 视口;祖先绘制特效启用时使用 Raster 供外层捕获,保留请求的渲染模式。

Backend activeBackend() const

实际渲染后端 Raster 或 OpenGL;只读,在视图显示并完成初始化前为 Raster。

QString rendererName() const

当前 OpenGL 设备名称;只读,Raster 或尚未初始化时为空。

QString fallbackReason() const

自动回退后端的原因;只读,没有回退原因时为空。

信号

void spatialEnabledChanged(bool enabled)

是否启用透视;动效和主题策略允许时默认 true。 关闭后将同一批控件放回原生纵向布局,保留原有状态。

void cameraDistanceChanged(qreal distance)

透视距离,逻辑像素范围 [100, 10000],默认 1000;越小,近大远小越明显。

void zoomChanged(qreal zoom)

整个场景的显示倍率 [0.1, 4],默认 1;不改变条目的位置和姿态。

void pointerTrackingEnabledChanged(bool enabled)

是否让整个场景随鼠标倾斜,默认 true,仅在 3D 中生效。

void maximumTiltChanged(const QPointF& degrees)

鼠标跟随的俯仰/偏航上限,各为 [0, 45] 度,默认 (10, 16)。

void responseTimeChanged(int milliseconds)

指针跟随时间常数 [0, 1000] 毫秒,默认 140;越大越柔和,0 表示立即跟随。 此值是指数响应的时间常数,不是固定动画时长。

void maximumFrameRateChanged(int framesPerSecond)

鼠标动画更新上限 [15, 120] 帧/秒,默认 60,不代表显示器实际帧率。

void cacheEnabledChanged(bool enabled)

是否缓存控件表面,默认 true;内容发生变化时仍会重绘。

void renderModeChanged(RenderMode mode)

请求的后端:Auto(默认)、Raster 或 OpenGL;OpenGL 不可用时回退。 切换等待当前鼠标操作结束;使用 activeBackend 查询实际后端。隐藏或完全裁出时释放 GPU 视口;祖先绘制特效启用时使用 Raster 供外层捕获,保留请求的渲染模式。

void rendererChanged()

实际渲染后端 Raster 或 OpenGL;只读,在视图显示并完成初始化前为 Raster。

void itemCountChanged(int count)

承载的表面数量,包含隐藏条目;只读,初始为 0。 外部销毁控件时立即移除条目,销毁完成后合并通知,允许回调销毁宿主。

枚举

enum class RenderMode
  • Auto
  • Raster
  • OpenGL
enum class Backend
  • Raster
  • OpenGL

析构函数

PySide6 扩展

addOwnedWidget(widget)

Transfer content to the view; releasing its item destroys it.

addReparentedWidget(widget)

Restore content to its original QWidget parent on release.

子类扩展接口

方法

继承自 fluent::FluentElement

方法

static void setTheme(Theme theme)

更新全局主题,并通知所有活跃的 FluentElement 实例。

static void setThemeDeferred(Theme theme)

以可见元素优先的批次调度主题刷新,避免阻塞当前交互。

static Theme currentTheme()

返回当前生效的全局主题。

static bool themeUsesDarkAppearance(Theme theme)

返回指定主题是否使用暗色底的 chrome 与材质 token。

static void refreshTheme()

在设计 token 变化后重新广播当前主题以触发重绘。

static int themeGeneration()

每次主题变化自增的单调计数器,用于过期判断。

Theme effectiveTheme() const

解析此元素实际使用的主题,会优先读取 QWidget 父链上的局部主题覆盖。

bool effectiveThemeUsesDarkAppearance() const

返回此元素的实际主题是否使用暗色底 chrome。

bool setThemeOverrides(const QJsonObject& overrides)

供组件侧访问的设计 token 接口。

QJsonObject themeOverrides() const

返回当前元素的覆盖配置。

void clearThemeOverrides()

恢复全部 token 跟随全局主题。

const Colors& themeColorsRef() const

对当前色板的零拷贝访问(用于绘制热路径)。

Material::AcrylicToken themeAcrylic() const

材质、阴影和自适应布局 token。

QColor themeBackdrop(bool active) const

给定激活状态下的窗口 chrome 背景色。

virtual void onThemeUpdated()

全局主题变化后调用。

枚举

enum Theme

解析设计 token 时使用的全局视觉主题。

  • Light = 0
  • Dark = 1
  • HighContrast = 2

类型与别名

struct Colors

返回给组件使用的设计 token 快照。

继承自 fluent::QMLPlus

方法

AnchorLayout::Anchors* anchors()

返回 QWidget 宿主的可变锚点配置。

void setState(const QString& name)

应用具名 state;名称为空时恢复默认状态。

void addState(const QMLState& state)

注册或替换一个具名 state 定义。

void evaluateStates()

重新计算条件;按注册顺序选择首个成立的条件。

QMLPlusRuntime* runtime()

返回按需创建、由宿主持有的状态观察对象。

void setAnchors(const AnchorLayout::Anchors& anchors)

替换宿主的全部锚点,并使父布局失效。

void resetAnchors()

清除宿主全部锚点,并使父布局失效。

void addTransition(const StateTransition& transition)

按优先顺序注册状态过渡。

void setBehavior(const char* propertyName, const PropertyAnimation& animation)

设置宿主受管理属性写入时使用的默认动画。

void clearBehavior(const char* propertyName)

移除后续受管理写入使用的默认动画。

bool writeProperty(const char* propertyName, const QVariant& value)

通过 Behavior 写入宿主属性,且不移除该属性的绑定。

Qt 基类完整成员:QWidget