change: UI and migrate to gtest

This commit is contained in:
2026-07-27 05:27:32 +08:00
parent dd5508e571
commit a84e75ef47
56 changed files with 4484 additions and 4949 deletions
+155 -2
View File
@@ -176,6 +176,10 @@ ViewerWidget::ViewerWidget(ViewerDisplayWidget *display, QWidget *parent)
, multicam_panel_(nullptr)
, bridge_(new EngineEventBridge(this))
, audio_processor_(oakengine_audio_processor_create())
, overlay_(nullptr)
, info_chip_(nullptr)
, safe_frame_btn_(nullptr)
, overlay_zoom_index_(5)
{
// Set up main layout
QVBoxLayout *layout = new QVBoxLayout(this);
@@ -224,6 +228,9 @@ ViewerWidget::ViewerWidget(ViewerDisplayWidget *display, QWidget *parent)
&ViewerSizer::hand_drag_move);
sizer_->set_widget(display_widget_);
// Create the design-reference overlay (info chip + zoom/safe-frame buttons)
create_overlay();
// Make the display widget the first tabbable widget. While the viewer display cannot actually
// be interacted with by tabbing, it prevents the actual first tabbable widget (the playhead
// slider in `controls_`) from getting auto-focused any time the panel is maximized (with `)
@@ -234,8 +241,13 @@ ViewerWidget::ViewerWidget(ViewerDisplayWidget *display, QWidget *parent)
connect_timeline_view(waveform_view_);
layout->addWidget(waveform_view_);
// Create time ruler
layout->addWidget(ruler());
// Per the Oak UI design reference the viewer no longer shows its own time
// ruler: it duplicates the timeline's ruler and clutters the viewer. The
// ruler object still exists (created by TimeBasedWidget) and continues to
// back marker/work-area/snapping logic and the scrollbar; we simply do not
// add it to the visible layout. Scrubbing is provided by the transport
// controls and the scrollbar below.
// layout->addWidget(ruler());
// Create scrollbar
layout->addWidget(scrollbar());
@@ -493,6 +505,8 @@ void ViewerWidget::resizeEvent(QResizeEvent *event)
super::resizeEvent(event);
update_minimum_scale();
position_overlay();
}
OakEnginePreviewRequest *ViewerWidget::get_single_frame(const Rational &t,
@@ -2260,6 +2274,8 @@ void ViewerWidget::set_viewer_resolution(int width, int height)
foreach (ViewerWindow *vw, windows_) {
vw->set_resolution(width, height);
}
update_info_chip();
}
void ViewerWidget::set_viewer_pixel_aspect(const Rational &ratio)
@@ -2323,6 +2339,143 @@ void ViewerWidget::set_zoom_from_menu(QAction *action)
s.height() / 2);
}
void ViewerWidget::create_overlay()
{
// A thin transparent strip laid over the top of the viewer display. Left
// side carries an info chip (resolution + frame rate); right side carries
// zoom in/out/fit and a safe-frame toggle, per the UI design reference.
overlay_ = new QWidget(sizer_);
overlay_->setAttribute(Qt::WA_TransparentForMouseEvents, false);
overlay_->setAutoFillBackground(false);
auto *overlay_layout = new QHBoxLayout(overlay_);
overlay_layout->setContentsMargins(6, 4, 6, 4);
overlay_layout->setSpacing(4);
info_chip_ = new QLabel(overlay_);
info_chip_->setStyleSheet(QStringLiteral(
"QLabel { background-color: rgba(0,0,0,160); color: white; "
"border-radius: 3px; padding: 2px 6px; }"));
info_chip_->setVisible(false);
overlay_layout->addWidget(info_chip_);
overlay_layout->addStretch();
const QString btn_style = QStringLiteral(
"QToolButton { background-color: rgba(0,0,0,160); color: white; "
"border: none; border-radius: 3px; padding: 2px 6px; } "
"QToolButton:hover { background-color: rgba(0,0,0,200); } "
"QToolButton:checked { background-color: rgba(0,120,215,200); }");
auto make_btn = [this, &btn_style](const QString &text,
const QString &tooltip) {
auto *b = new QToolButton(overlay_);
b->setText(text);
b->setToolTip(tooltip);
b->setStyleSheet(btn_style);
b->setAutoRaise(false);
return b;
};
QToolButton *zoom_out_btn = make_btn(QStringLiteral("\u2212"), tr("Zoom Out"));
QToolButton *zoom_in_btn = make_btn(QStringLiteral("+"), tr("Zoom In"));
QToolButton *zoom_fit_btn = make_btn(QStringLiteral("\u2922"), tr("Fit to Window"));
safe_frame_btn_ = make_btn(QStringLiteral("\u25A2"), tr("Safe Margins"));
safe_frame_btn_->setCheckable(true);
overlay_layout->addWidget(zoom_out_btn);
overlay_layout->addWidget(zoom_in_btn);
overlay_layout->addWidget(zoom_fit_btn);
overlay_layout->addWidget(safe_frame_btn_);
connect(zoom_out_btn, &QToolButton::clicked, this,
&ViewerWidget::overlay_zoom_out);
connect(zoom_in_btn, &QToolButton::clicked, this,
&ViewerWidget::overlay_zoom_in);
connect(zoom_fit_btn, &QToolButton::clicked, this,
&ViewerWidget::overlay_zoom_fit);
connect(safe_frame_btn_, &QToolButton::toggled, this,
[this](bool) { overlay_toggle_safe_frame(); });
overlay_->raise();
}
void ViewerWidget::position_overlay()
{
if (!overlay_ || !sizer_) {
return;
}
// Span the full width of the sizer, hugging the top edge.
overlay_->setGeometry(0, 0, sizer_->width(), overlay_->sizeHint().height());
overlay_->raise();
}
void ViewerWidget::update_info_chip()
{
if (!info_chip_) {
return;
}
if (!get_connected_node()) {
info_chip_->setVisible(false);
return;
}
VideoParams vp = viewer_output_video_params(get_connected_node());
if (vp.width() <= 0 || vp.height() <= 0) {
info_chip_->setVisible(false);
return;
}
const double fps = vp.frame_rate().to_double();
const double fps_rounded = qRound(fps);
QString fps_str = (qFuzzyCompare(fps, fps_rounded))
? QString::number(static_cast<int>(fps_rounded))
: QString::number(fps, 'f', 2);
info_chip_->setText(QStringLiteral("%1\u00d7%2 \u00b7 %3 FPS")
.arg(vp.width())
.arg(vp.height())
.arg(fps_str));
info_chip_->setVisible(true);
}
void ViewerWidget::overlay_zoom_in()
{
if (overlay_zoom_index_ < ViewerSizer::k_zoom_level_count - 1) {
overlay_zoom_index_++;
}
auto s = sizer_->get_container_size();
sizer_->set_zoom_anchored(ViewerSizer::k_zoom_levels[overlay_zoom_index_],
s.width() / 2, s.height() / 2);
}
void ViewerWidget::overlay_zoom_out()
{
if (overlay_zoom_index_ > 0) {
overlay_zoom_index_--;
}
auto s = sizer_->get_container_size();
sizer_->set_zoom_anchored(ViewerSizer::k_zoom_levels[overlay_zoom_index_],
s.width() / 2, s.height() / 2);
}
void ViewerWidget::overlay_zoom_fit()
{
// The zoom menu uses -1 to mean "fit to window".
auto s = sizer_->get_container_size();
sizer_->set_zoom_anchored(-1, s.width() / 2, s.height() / 2);
}
void ViewerWidget::overlay_toggle_safe_frame()
{
if (!safe_frame_btn_) {
return;
}
display_widget_->set_safe_margins(
ViewerSafeMarginInfo(safe_frame_btn_->isChecked()));
}
void ViewerWidget::viewer_invalidated_video_range(const TimeRange &range)
{
// If our current frame is within this range, we need to update