fix(collab): name badges render for real — text overlay above a depth-offset shapes overlay
The full root-cause chain of the empty/mangled badges: - GAL text justify is painter-residue → PRESENCE_TEXT_OVERLAY pins TOP-LEFT. - VIEW_OVERLAY::ViewDraw hard-sets EVERY overlay to GetMinDepth(), so the shapes and text overlays always collided at one depth, where later-drawn fragments lose (and bitmap glyphs are textured quads whose transparent cells also write depth — punch-through produced cell-shaped holes instead). Fix rides the new fork VIEW_OVERLAY::SetDepthOffset: shapes at min+1, labels at min — 'rect first, text on top' now holds regardless of paint order. Verified: chips contain crisp names (dark-on-light, white-on-dark) for selection tags and cursor labels; presence suites 10/10. - kicad pointer bump (fork 24c5854d5b). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvqUd4QsJSGHN28aunJRTq
This commit is contained in:
parent
4c287c2a27
commit
c8c2c5f583
4 changed files with 89 additions and 52 deletions
2
kicad
2
kicad
|
|
@ -1 +1 @@
|
|||
Subproject commit 4a7250d11a91042ba7cb58e3962881373a1c904d
|
||||
Subproject commit 24c5854d5bf76fc908b8ba8a3bed63675d5067cf
|
||||
|
|
@ -77,6 +77,7 @@ struct STYLE
|
|||
double pinRingAlpha = 0.9;
|
||||
double pinFillAlpha = 1.0;
|
||||
double pinResolvedAlpha = 0.3;
|
||||
|
||||
};
|
||||
|
||||
inline KIGFX::COLOR4D parseHexColor( const std::string& aHex, const KIGFX::COLOR4D& aFallback )
|
||||
|
|
@ -179,14 +180,21 @@ inline KIGFX::COLOR4D chipTextColor( const KIGFX::COLOR4D& aBg )
|
|||
}
|
||||
|
||||
/**
|
||||
* VIEW_OVERLAY that PINS the GAL text justify before executing its commands.
|
||||
* A plain overlay draws with whatever justify the last painter left in the
|
||||
* GAL (CENTER is only the reset default), so BitmapText anchoring was
|
||||
* nondeterministic — labels wandered around their chips depending on what
|
||||
* rendered before them. This guarantees TOP-LEFT anchoring for every
|
||||
* BitmapText below; the label math is written against that.
|
||||
* The TEXT half of the presence drawing. All labels go through this overlay
|
||||
* because a plain VIEW_OVERLAY has two text hazards:
|
||||
* - it draws with whatever justify the last painter left in the GAL
|
||||
* (CENTER is only the reset default) → anchoring was nondeterministic;
|
||||
* this pins TOP-LEFT, which the label math is written against.
|
||||
* - every overlay draws its whole command list at ONE depth
|
||||
* (VIEW_OVERLAY::ViewDraw hard-sets GetMinDepth()) and same-depth
|
||||
* fragments drawn later LOSE the depth test; bitmap glyphs are textured
|
||||
* QUADS whose transparent cells also write depth, so text can neither be
|
||||
* drawn under a chip (erased) nor over one (punches cell-shaped holes).
|
||||
* The SHAPES overlay is therefore pushed DEEPER via the fork's
|
||||
* VIEW_OVERLAY::SetDepthOffset (chips at min+1, text at min) — "rect
|
||||
* first, text on top" then holds regardless of paint order.
|
||||
*/
|
||||
class PRESENCE_OVERLAY : public KIGFX::VIEW_OVERLAY
|
||||
class PRESENCE_TEXT_OVERLAY : public KIGFX::VIEW_OVERLAY
|
||||
{
|
||||
public:
|
||||
void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override
|
||||
|
|
@ -198,20 +206,24 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/** VIEW::MakeOverlay's body, for our subclass (make + Add to the view). */
|
||||
inline std::shared_ptr<PRESENCE_OVERLAY> makePresenceOverlay( KIGFX::VIEW* aView )
|
||||
/** Depth offset for the SHAPES overlay — one unit deeper than the text. */
|
||||
constexpr double PRESENCE_SHAPES_DEPTH_OFFSET = 1.0;
|
||||
|
||||
/** VIEW::MakeOverlay's body, for the text overlay (make + Add to the view). */
|
||||
inline std::shared_ptr<PRESENCE_TEXT_OVERLAY> makePresenceTextOverlay( KIGFX::VIEW* aView )
|
||||
{
|
||||
auto overlay = std::make_shared<PRESENCE_OVERLAY>();
|
||||
auto overlay = std::make_shared<PRESENCE_TEXT_OVERLAY>();
|
||||
aView->Add( overlay.get() );
|
||||
return overlay;
|
||||
}
|
||||
|
||||
/** Name tag next to (or inside) a box, per the label placement knobs. `px` is
|
||||
* world-units-per-screen-pixel. Text anchoring is TOP-LEFT — guaranteed by
|
||||
* PRESENCE_OVERLAY pinning the GAL justify (a plain overlay inherits
|
||||
* whatever the last painter left, which made anchoring nondeterministic). */
|
||||
inline void drawLabel( KIGFX::VIEW_OVERLAY* aOv, const BOX2I& aBox, const std::string& aText,
|
||||
const KIGFX::COLOR4D& aColor, double aPx, const STYLE& aS )
|
||||
* world-units-per-screen-pixel. The chip rect goes to the SHAPES overlay,
|
||||
* the text to the TEXT overlay (nearest depth + pinned TOP-LEFT justify —
|
||||
* see PRESENCE_TEXT_OVERLAY), so the text always renders on top. */
|
||||
inline void drawLabel( KIGFX::VIEW_OVERLAY* aOv, KIGFX::VIEW_OVERLAY* aTextOv, const BOX2I& aBox,
|
||||
const std::string& aText, const KIGFX::COLOR4D& aColor, double aPx,
|
||||
const STYLE& aS )
|
||||
{
|
||||
if( !aS.labelShow || aText.empty() )
|
||||
return;
|
||||
|
|
@ -234,17 +246,6 @@ inline void drawLabel( KIGFX::VIEW_OVERLAY* aOv, const BOX2I& aBox, const std::s
|
|||
else
|
||||
y = aS.labelInside ? aBox.GetEnd().y - off - h : aBox.GetEnd().y + off;
|
||||
|
||||
// The whole overlay draws at ONE depth: same-depth fragments drawn LATER
|
||||
// lose the depth test, so anything covering the text erases it. Draw the
|
||||
// TEXT FIRST and the chip rect AFTER — the rect is rejected exactly on
|
||||
// the glyph pixels, punching the text through the chip.
|
||||
aOv->SetIsStroke( true );
|
||||
aOv->SetIsFill( false );
|
||||
aOv->SetStrokeColor( aS.labelChip ? chipTextColor( aColor ) : aColor );
|
||||
aOv->SetGlyphSize( VECTOR2I( KiROUND( h ), KiROUND( h ) ) );
|
||||
aOv->BitmapText( wxString::FromUTF8( aText.c_str() ), VECTOR2I( KiROUND( x ), KiROUND( y ) ),
|
||||
ANGLE_0 );
|
||||
|
||||
if( aS.labelChip )
|
||||
{
|
||||
double padX = 3 * aPx, padY = 2 * aPx;
|
||||
|
|
@ -256,14 +257,21 @@ inline void drawLabel( KIGFX::VIEW_OVERLAY* aOv, const BOX2I& aBox, const std::s
|
|||
aOv->SetIsStroke( true );
|
||||
aOv->SetIsFill( false );
|
||||
}
|
||||
|
||||
aTextOv->SetIsStroke( true );
|
||||
aTextOv->SetIsFill( false );
|
||||
aTextOv->SetStrokeColor( aS.labelChip ? chipTextColor( aColor ) : aColor );
|
||||
aTextOv->SetGlyphSize( VECTOR2I( KiROUND( h ), KiROUND( h ) ) );
|
||||
aTextOv->BitmapText( wxString::FromUTF8( aText.c_str() ),
|
||||
VECTOR2I( KiROUND( x ), KiROUND( y ) ), ANGLE_0 );
|
||||
}
|
||||
|
||||
/** Selection highlight for one item, in the chosen shape. `aOutline` is the
|
||||
* item's exact geometry for selShape 5 (pcbnew supplies it; eeschema passes
|
||||
* nullptr and shape 5 falls back to the bbox rectangle). */
|
||||
inline void drawSelectionBox( KIGFX::VIEW_OVERLAY* aOv, BOX2I aBox, const std::string& aName,
|
||||
const KIGFX::COLOR4D& aColor, double aPx, const STYLE& aS,
|
||||
const SHAPE_POLY_SET* aOutline = nullptr )
|
||||
inline void drawSelectionBox( KIGFX::VIEW_OVERLAY* aOv, KIGFX::VIEW_OVERLAY* aTextOv, BOX2I aBox,
|
||||
const std::string& aName, const KIGFX::COLOR4D& aColor, double aPx,
|
||||
const STYLE& aS, const SHAPE_POLY_SET* aOutline = nullptr )
|
||||
{
|
||||
if( aS.selShape == 5 && aOutline && aOutline->OutlineCount() > 0 )
|
||||
{
|
||||
|
|
@ -276,7 +284,7 @@ inline void drawSelectionBox( KIGFX::VIEW_OVERLAY* aOv, BOX2I aBox, const std::s
|
|||
|
||||
BOX2I labelBox = aOutline->BBox();
|
||||
labelBox.Inflate( KiROUND( aS.selPaddingPx * aPx ) );
|
||||
drawLabel( aOv, labelBox, aName, aColor, aPx, aS );
|
||||
drawLabel( aOv, aTextOv, labelBox, aName, aColor, aPx, aS );
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -356,11 +364,12 @@ inline void drawSelectionBox( KIGFX::VIEW_OVERLAY* aOv, BOX2I aBox, const std::s
|
|||
}
|
||||
}
|
||||
|
||||
drawLabel( aOv, aBox, aName, aColor, aPx, aS );
|
||||
drawLabel( aOv, aTextOv, aBox, aName, aColor, aPx, aS );
|
||||
}
|
||||
|
||||
/** Remote cursor (+ name label) in the chosen shape. */
|
||||
inline void drawCursor( KIGFX::VIEW_OVERLAY* aOv, const VECTOR2D& aPos, const std::string& aName,
|
||||
inline void drawCursor( KIGFX::VIEW_OVERLAY* aOv, KIGFX::VIEW_OVERLAY* aTextOv,
|
||||
const VECTOR2D& aPos, const std::string& aName,
|
||||
const KIGFX::COLOR4D& aColor, double aPx, const STYLE& aS )
|
||||
{
|
||||
double s = aS.cursorSizePx * aPx;
|
||||
|
|
@ -407,15 +416,8 @@ inline void drawCursor( KIGFX::VIEW_OVERLAY* aOv, const VECTOR2D& aPos, const st
|
|||
VECTOR2D at = aPos + VECTOR2D( ( aS.cursorSizePx + 4 ) * aPx,
|
||||
( aS.cursorSizePx + 4 ) * aPx );
|
||||
|
||||
// Text FIRST, chip rect AFTER — see drawLabel (same-depth z rejection
|
||||
// punches the text through the chip).
|
||||
aOv->SetIsStroke( true );
|
||||
aOv->SetIsFill( false );
|
||||
aOv->SetStrokeColor( aS.cursorLabelChip ? chipTextColor( aColor ) : c );
|
||||
aOv->SetGlyphSize( VECTOR2I( KiROUND( h ), KiROUND( h ) ) );
|
||||
aOv->BitmapText( wxString::FromUTF8( aName.c_str() ),
|
||||
VECTOR2I( KiROUND( at.x ), KiROUND( at.y ) ), ANGLE_0 );
|
||||
|
||||
// Chip rect on the shapes overlay, text on the TEXT overlay (nearest
|
||||
// depth) — text always renders on top of its chip.
|
||||
if( aS.cursorLabelChip )
|
||||
{
|
||||
double padX = 3 * aPx, padY = 2 * aPx;
|
||||
|
|
@ -427,6 +429,13 @@ inline void drawCursor( KIGFX::VIEW_OVERLAY* aOv, const VECTOR2D& aPos, const st
|
|||
aOv->SetIsStroke( true );
|
||||
aOv->SetIsFill( false );
|
||||
}
|
||||
|
||||
aTextOv->SetIsStroke( true );
|
||||
aTextOv->SetIsFill( false );
|
||||
aTextOv->SetStrokeColor( aS.cursorLabelChip ? chipTextColor( aColor ) : c );
|
||||
aTextOv->SetGlyphSize( VECTOR2I( KiROUND( h ), KiROUND( h ) ) );
|
||||
aTextOv->BitmapText( wxString::FromUTF8( aName.c_str() ),
|
||||
VECTOR2I( KiROUND( at.x ), KiROUND( at.y ) ), ANGLE_0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -720,6 +720,9 @@ std::vector<PIN> g_pins;
|
|||
// — see collab_presence_style.h; live-patched by kicadCollabSetStyle (tuner).
|
||||
pcbjam_presence::STYLE g_style;
|
||||
std::shared_ptr<KIGFX::VIEW_OVERLAY> g_overlay;
|
||||
// Labels render from their own overlay at the nearest depth (chip rects
|
||||
// would otherwise erase same-depth text) — see PRESENCE_TEXT_OVERLAY.
|
||||
std::shared_ptr<KIGFX::VIEW_OVERLAY> g_textOverlay;
|
||||
bool g_started = false;
|
||||
bool g_redrawScheduled = false;
|
||||
bool g_selCheckScheduled = false;
|
||||
|
|
@ -799,9 +802,18 @@ void redrawOverlay()
|
|||
KIGFX::VIEW* view = fr->GetCanvas()->GetView();
|
||||
|
||||
if( !g_overlay )
|
||||
g_overlay = pcbjam_presence::makePresenceOverlay( view );
|
||||
{
|
||||
g_overlay = view->MakeOverlay();
|
||||
// Shapes sit one depth unit BELOW the text overlay (fork
|
||||
// SetDepthOffset) so labels always render on top of chips/boxes.
|
||||
g_overlay->SetDepthOffset( pcbjam_presence::PRESENCE_SHAPES_DEPTH_OFFSET );
|
||||
}
|
||||
|
||||
if( !g_textOverlay )
|
||||
g_textOverlay = pcbjam_presence::makePresenceTextOverlay( view );
|
||||
|
||||
g_overlay->Clear();
|
||||
g_textOverlay->Clear();
|
||||
|
||||
// Screen-constant sizing via the GAL matrix (GetScale() is the zoom, not px/IU).
|
||||
double px = view->ToWorld( 1.0 );
|
||||
|
|
@ -821,13 +833,14 @@ void redrawOverlay()
|
|||
if( !item )
|
||||
continue; // not in this schematic (yet) — skip silently
|
||||
|
||||
pcbjam_presence::drawSelectionBox( g_overlay.get(), item->ViewBBox(), peer.name,
|
||||
color, px, g_style );
|
||||
pcbjam_presence::drawSelectionBox( g_overlay.get(), g_textOverlay.get(),
|
||||
item->ViewBBox(), peer.name, color, px,
|
||||
g_style );
|
||||
}
|
||||
|
||||
if( peer.hasCursor )
|
||||
pcbjam_presence::drawCursor( g_overlay.get(), peer.cursor, peer.name, color, px,
|
||||
g_style );
|
||||
pcbjam_presence::drawCursor( g_overlay.get(), g_textOverlay.get(), peer.cursor,
|
||||
peer.name, color, px, g_style );
|
||||
}
|
||||
|
||||
// Comment pin dots (0005), drawn last so they sit above selection outlines.
|
||||
|
|
@ -838,6 +851,7 @@ void redrawOverlay()
|
|||
}
|
||||
|
||||
view->Update( g_overlay.get() );
|
||||
view->Update( g_textOverlay.get() );
|
||||
fr->GetCanvas()->ForceRefresh();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1126,6 +1126,9 @@ std::vector<PIN> g_pins;
|
|||
// — see collab_presence_style.h; live-patched by kicadCollabSetStyle (tuner).
|
||||
pcbjam_presence::STYLE g_style;
|
||||
std::shared_ptr<KIGFX::VIEW_OVERLAY> g_overlay;
|
||||
// Labels render from their own overlay at the nearest depth (chip rects
|
||||
// would otherwise erase same-depth text) — see PRESENCE_TEXT_OVERLAY.
|
||||
std::shared_ptr<KIGFX::VIEW_OVERLAY> g_textOverlay;
|
||||
bool g_started = false;
|
||||
bool g_redrawScheduled = false;
|
||||
bool g_selCheckScheduled = false;
|
||||
|
|
@ -1207,9 +1210,18 @@ void redrawOverlay()
|
|||
KIGFX::VIEW* view = fr->GetCanvas()->GetView();
|
||||
|
||||
if( !g_overlay )
|
||||
g_overlay = pcbjam_presence::makePresenceOverlay( view );
|
||||
{
|
||||
g_overlay = view->MakeOverlay();
|
||||
// Shapes sit one depth unit BELOW the text overlay (fork
|
||||
// SetDepthOffset) so labels always render on top of chips/boxes.
|
||||
g_overlay->SetDepthOffset( pcbjam_presence::PRESENCE_SHAPES_DEPTH_OFFSET );
|
||||
}
|
||||
|
||||
if( !g_textOverlay )
|
||||
g_textOverlay = pcbjam_presence::makePresenceTextOverlay( view );
|
||||
|
||||
g_overlay->Clear();
|
||||
g_textOverlay->Clear();
|
||||
|
||||
BOARD* board = fr->GetBoard();
|
||||
// Screen-constant sizing: px → world units, so cursors/outline widths don't
|
||||
|
|
@ -1265,13 +1277,14 @@ void redrawOverlay()
|
|||
}
|
||||
}
|
||||
|
||||
pcbjam_presence::drawSelectionBox( g_overlay.get(), item->ViewBBox(), peer.name,
|
||||
color, px, g_style, &outline );
|
||||
pcbjam_presence::drawSelectionBox( g_overlay.get(), g_textOverlay.get(),
|
||||
item->ViewBBox(), peer.name, color, px,
|
||||
g_style, &outline );
|
||||
}
|
||||
|
||||
if( peer.hasCursor )
|
||||
pcbjam_presence::drawCursor( g_overlay.get(), peer.cursor, peer.name, color, px,
|
||||
g_style );
|
||||
pcbjam_presence::drawCursor( g_overlay.get(), g_textOverlay.get(), peer.cursor,
|
||||
peer.name, color, px, g_style );
|
||||
}
|
||||
|
||||
// Comment pin dots (0005), drawn last so they sit above selection outlines.
|
||||
|
|
@ -1282,6 +1295,7 @@ void redrawOverlay()
|
|||
}
|
||||
|
||||
view->Update( g_overlay.get() );
|
||||
view->Update( g_textOverlay.get() );
|
||||
// The canvas repaints on its own only with focus/input — force it, exactly as
|
||||
// the cross-probe flash does.
|
||||
fr->GetCanvas()->ForceRefresh();
|
||||
|
|
|
|||
Loading…
Reference in a new issue