ui/widget/material/base/focus_ring.cpp¶
Material Design Focus Indicator Implementation. More...
Namespaces¶
| Name |
|---|
| cf |
| cf::ui |
| cf::ui::widget |
| cf::ui::widget::material |
| cf::ui::widget::material::base |
Detailed Description¶
Material Design Focus Indicator Implementation.
Author: Material Design Framework Team
Version: 0.1
Since: 0.1
Date: 2026-02-28
Draws focus ring following Material Design 3 specifications. Ring width: 3dp, inset: 3dp from widget boundary.
Source code¶
#include "focus_ring.h"
#include "base/device_pixel.h"
#include "components/material/cfmaterial_animation_factory.h"
#include "components/timing_animation.h"
#include <QPainter>
#include <QPainterPath>
#include <QPainterPathStroker>
namespace cf::ui::widget::material::base {
using namespace cf::ui::components::material;
using namespace cf::ui::components;
using namespace cf::ui::base;
MdFocusIndicator::MdFocusIndicator(
cf::WeakPtr<components::material::CFMaterialAnimationFactory> factory, QObject* parent)
: QObject(parent), m_progress(0.0f), m_animator(factory) {}
// ============================================================================
// Event Handlers
// ============================================================================
void MdFocusIndicator::onFocusIn() {
auto* factory = m_animator.Get();
if (!factory || !factory->isAllEnabled()) {
// Direct set if animations disabled
m_progress = 1.0f;
return;
}
// Get fade animation for ring appearance
auto anim = factory->getAnimation("md.animation.fadeIn");
if (!anim) {
m_progress = 1.0f;
return;
}
// Get raw pointer and set range if it's a timing animation
auto* rawAnim = anim.Get();
auto* timingAnim = static_cast<components::ICFTimingAnimation*>(rawAnim);
if (timingAnim) {
timingAnim->setRange(0.0f, 1.0f);
}
connect(rawAnim, &components::ICFAbstractAnimation::progressChanged, this,
[this](float progress) { m_progress = progress; });
rawAnim->start(components::ICFAbstractAnimation::Direction::Forward);
}
void MdFocusIndicator::onFocusOut() {
auto* factory = m_animator.Get();
if (!factory || !factory->isAllEnabled()) {
// Direct set if animations disabled
m_progress = 0.0f;
return;
}
// Get fade animation for ring disappearance
auto anim = factory->getAnimation("md.animation.fadeOut");
if (!anim) {
m_progress = 0.0f;
return;
}
// Get raw pointer and set range if it's a timing animation
auto* rawAnim = anim.Get();
auto* timingAnim = static_cast<components::ICFTimingAnimation*>(rawAnim);
if (timingAnim) {
timingAnim->setRange(1.0f, 0.0f);
}
connect(rawAnim, &components::ICFAbstractAnimation::progressChanged, this,
[this](float progress) { m_progress = progress; });
rawAnim->start(components::ICFAbstractAnimation::Direction::Forward);
}
// ============================================================================
// Painting
// ============================================================================
void MdFocusIndicator::paint(QPainter* painter, const QPainterPath& shape,
const CFColor& indicatorColor) {
if (m_progress <= 0.0f || !painter) {
return;
}
// Convert dp to pixels
// Note: Using device pixel ratio 1.0 for now. In production, get from QApplication.
device::CanvasUnitHelper helper(1.0);
float ringWidth = helper.dpToPx(3.0f); // 3dp ring width
float inset = helper.dpToPx(3.0f); // 3dp inset
painter->save();
// Create stroker for the ring
QPainterPathStroker stroker;
stroker.setWidth(ringWidth);
stroker.setCapStyle(Qt::SquareCap);
stroker.setJoinStyle(Qt::MiterJoin);
// Create offset path (inset from widget boundary)
QPainterPathStroker insetStroker;
insetStroker.setWidth(inset * 2); // Inset on both sides
QPainterPath innerShape = insetStroker.createStroke(shape);
// Subtract to get the inset outline
QPainterPath outlinePath = shape.subtracted(innerShape);
// Create the ring path
QPainterPath ringPath = stroker.createStroke(outlinePath);
// Apply opacity based on animation progress
QColor color = indicatorColor.native_color();
color.setAlphaF(color.alphaF() * m_progress);
// Draw the ring
painter->setPen(Qt::NoPen);
painter->setBrush(color);
painter->drawPath(ringPath);
painter->restore();
}
} // namespace cf::ui::widget::material::base
Updated on 2026-03-09 at 10:14:01 +0000