Module: Tuile::StrictLayout

Defined in:
lib/tuile/strict_layout.rb,
sig/tuile.rbs

Overview

The stale-rect diagnostic: a Component#rect read taken while an ancestor still owes a Component#relayout says so, instead of handing back the previous pass's rectangle in silence. On wherever a FakeScreen is the installed screen — a spec suite needs no setup to get it — and off in an app, which strict_layout overrides either way:

holder.constrain(pane, Rect.new(0, 0, 100, 26))
pane.left.rect        # => Tuile::Error: read the rect of #<Tuile::Component::Label
                    #    rect=(0,0 80x50)> while #<TwoPane rect=(0,0 100x26)> owes a
                    #    relayout … at spec/two_pane_spec.rb:42

:warn logs the same line to logger and hands the rectangle over, for watching a running app; :raise is the default and what a spec wants, since the backtrace names the read and a suite that never set a logger would see nothing at all. For the read that is pre-settle on purpose, without_strict_layout.

Reading size, width, height, local_rect, absolute_rect or to_screen reports too — they all go through the one reader.

Only reads the app makes are reported (PLUMBING): a read lib/ makes on the app's behalf mid-handler is not the app's to fix.

Implementation details

strict_layout= and FakeScreen prepend this module into Component, so rect stays the bare attr_reader — the hottest read in the toolkit — in every process that never asks. Nothing unprepends; turning the mode off leaves the check inert. See D_strict_layout.

Constant Summary collapse

LIB_DIR =

Where the gem's own frames live, so the site the message names is the app's — to_screen and the size / width / height trio all read rect from inside component.rb.

Returns:

  • (String)
File.expand_path("..", __dir__)
REPORTING =

The thread-local marking a report in progress: Component#inspect prints the rect, so building the message re-enters rect on the very component that is being complained about.

Returns:

  • (Symbol)
:tuile_strict_layout_reporting
PLUMBING =

The readers that only forward to rect. A frame of one of these between the read and the app's own code carries no decision of the framework's, so the read still counts as the app's.

Returns:

  • (Array<String>)
%w[rect size width height local_rect local_extent_rect absolute_rect absolute_extent_rect
to_screen to_local].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.install ⇒ void

This method returns an undefined value.

Makes Component#rect consult Tuile.strict_layout — idempotent, and permanent for the process.



57
# File 'lib/tuile/strict_layout.rb', line 57

def install = Component.prepend(self)

.report(component, ancestor, mode) ⇒ void

This method returns an undefined value.

Reports component's rect read as stale, the way mode asks for.

@param component — the component whose rect was read.

@param ancestor — the ancestor owing the relayout.

@param mode — :raise or :warn.

Parameters:



66
67
68
69
70
71
72
73
# File 'lib/tuile/strict_layout.rb', line 66

def report(component, ancestor, mode)
  return unless app_read?

  message = message_for(component, ancestor)
  raise Error, message if mode == :raise

  Tuile.logger.warn(message)
end

Instance Method Details

#rect ⇒ Rect

Component#rect, reporting first when it is about to answer the previous pass's rectangle.

Returns:



118
119
120
121
122
123
124
125
# File 'lib/tuile/strict_layout.rb', line 118

def rect
  mode = Tuile.strict_layout
  if mode && !Thread.current[REPORTING]
    ancestor = stale_layout_ancestor
    StrictLayout.report(self, ancestor, mode) unless ancestor.nil?
  end
  super
end