> -- Иван Денисов писал(а):
> Я не понимаю, почему вы пишите, что контекст хранится в дочернем виде, когда он хранится в родительском.в том-то и шутка, что это как раз совершенно не обязательно (хранить в родительском), и живёт он именно в nested. смотрите:
## Код:
```
PROCEDURE NewContext (v: Views.View; base: View): Context;
      VAR c: Context;
   BEGIN
      NEW(c);
      c.view := v; c.base := base;
      v.InitContext(c); Stores.Join(v, base);
      RETURN c
   END NewContext;
```

вот так в ObxTwins создаётся nested view. `v.InitContext()` делает всего лишь: `v.context := context`.  и контекст этот, созданый, содержит информацию, которая нужна как раз родителю. и в родителе как раз его можно не кэшировать, а попросить у nested и кастануть во что надо. ObxTwins кэширует, но это чисто для удобства: основное место проживания у созданого контекста именно nested view, код можно переделать так, чтобы `top` и `bottom` были Views.View, и брать контексты из них. вот, собственно, так переделать:
## Код:
```
TYPE
      Identity = POINTER TO RECORD (Models.Model) END;   (* dummy model *)

      Context = POINTER TO RECORD (Models.Context)
         base: View;         (* container view *)
         view: Views.View;   (* contained view *)
         l, t, r, b: INTEGER   (* cached bounding box of contained view *)
      END;

      View = POINTER TO RECORD (Views.View)
         top, bottom: Views.View;
         ident: Identity;   (* temporary dummy model *)
         focus: Context   (* current focus; either top or bottom *)
      END;


   (* Context *)

   PROCEDURE (c: Context) ThisModel (): Models.Model;
   BEGIN
      RETURN NIL   (* don't give the embedded views information about the dummy twin model *)
   END ThisModel;

   PROCEDURE (c: Context) GetSize (OUT w, h: INTEGER);
   BEGIN
      w := c.r - c.l;
      h := c.b - c.t
   END GetSize;

   PROCEDURE (c: Context) MakeVisible (l, t, r, b: INTEGER);
      VAR w, h, sep: INTEGER;
   BEGIN
      IF c.base.top.context = c THEN   (* top view *)
         c.base.context.MakeVisible(l + border, t + border, r + border, b + border)
      ELSE   (* bottom view *)
         c.base.context.GetSize(w, h); sep := h DIV 3;
         c.base.context.MakeVisible(l + border, t + sep + border, r + border, b + sep + border)
      END
   END MakeVisible;

   PROCEDURE (c: Context) Consider (VAR p: Models.Proposal);
   BEGIN
      c.base.context.Consider(p)
   END Consider;

   PROCEDURE (c: Context) Normalize (): BOOLEAN;
   BEGIN
      RETURN c.base.context.Normalize()
   END Normalize;

   PROCEDURE NewContext (v: Views.View; base: View): Views.View;
      VAR c: Context;
   BEGIN
      NEW(c);
      c.view := v; c.base := base;
      v.InitContext(c); Stores.Join(v, base);
      RETURN v
   END NewContext;

   PROCEDURE CopyOf (source: Views.View; shallow: BOOLEAN; base: View): Views.View;
      VAR v: Views.View;
   BEGIN
      v := Views.CopyOf(source,  shallow);
      RETURN NewContext(v, base)
   END CopyOf;


   (* View *)

   PROCEDURE RecalcLayout (v: View);
      VAR w, h, sep: INTEGER; c: Context;
   BEGIN
      v.context.GetSize(w, h);
      sep := h DIV 3;   (* separate the two views at 1/3 of the container's height *)
      c := v.top.context(Context); c.l := border; c.t := border; c.r := w - border; c.b := sep - border;
      c := v.bottom.context(Context); c.l := border; c.t := sep + border; c.r := w - border; c.b := h - border
   END RecalcLayout;

   PROCEDURE SetFocus (v: Views.View; x, y: INTEGER): BOOLEAN;
      VAR p: Properties.FocusPref;
   BEGIN   (* determine whether v should be focused when the mouse is clicked at (x, y) in v *)
      p.hotFocus := FALSE;
      p.atLocation := TRUE; p.x := x; p.y := y;
      p.setFocus := FALSE;
      Views.HandlePropMsg(v, p);
      RETURN p.setFocus
   END SetFocus;

   PROCEDURE (v: View) CopyFromModelView (source: Views.View; model: Models.Model);
      VAR shallow: BOOLEAN;
   BEGIN
      WITH source: View DO
         shallow := model = source.ident;   (* shallow copy if both views share the same model *)
         Stores.Join(v, model);
         v.top := CopyOf(source.top, shallow, v);
         v.bottom := CopyOf(source.bottom, shallow, v);
         v.ident := model(Identity);
         v.focus := v.bottom.context(Context)
      END
   END CopyFromModelView;

   PROCEDURE (v: View) Internalize (VAR rd: Stores.Reader);
      VAR version: INTEGER; h: Views.View;
   BEGIN
      rd.ReadVersion(minVersion, maxVersion, version);
      IF ~rd.cancelled THEN
         Views.ReadView(rd, h); v.top := NewContext(h, v);
         Views.ReadView(rd, h); v.bottom := NewContext(h, v);
         NEW(v.ident); Stores.Join(v, v.ident);
         v.focus := v.bottom.context(Context)
      END
   END Internalize;

   PROCEDURE (v: View) Externalize (VAR wr: Stores.Writer);
   BEGIN
      wr.WriteVersion(maxVersion);
      Views.WriteView(wr, v.top);
      Views.WriteView(wr, v.bottom)
   END Externalize;

   PROCEDURE (v: View) Neutralize;
   BEGIN
      v.focus.view.Neutralize
   END Neutralize;

   PROCEDURE (v: View) ThisModel (): Models.Model;
   BEGIN
      RETURN v.ident
   END ThisModel;

   PROCEDURE (v: View) Restore (f: Views.Frame; l, t, r, b: INTEGER);
      VAR d: INTEGER; c: Context;
   BEGIN
      RecalcLayout(v);
      (* install the subframes for the subviews *)
      c := v.top.context(Context); Views.InstallFrame(f, c.view, c.l, c.t, 0, v.focus = c);
      c := v.bottom.context(Context); Views.InstallFrame(f, c.view, c.l, c.t, 1, v.focus = c);
      (* draw frame around the subviews *)
      d := 2 * f.dot;
      c := v.top.context(Context);
      IF (c.t - d < c.b + d) & (c.l - d < c.r + d) THEN f.DrawRect(c.l - d, c.t - d, c.r + d, c.b + d, f.dot, Ports.black) END;
      c := v.bottom.context(Context); 
      IF (c.t - d < c.b + d) & (c.l - d < c.r + d) THEN f.DrawRect(c.l - d, c.t - d, c.r + d, c.b + d, f.dot, Ports.black) END
   END Restore;

   PROCEDURE (v: View) HandleCtrlMsg (f: Views.Frame; VAR msg: Views.CtrlMessage;
                                             VAR focus: Views.View);
      VAR g: Views.Frame; newFocus: Context; mMsg: Controllers.MarkMsg;
         w, h, sep: INTEGER;
   BEGIN
      WITH msg: Controllers.CursorMessage DO
         v.context.GetSize(w, h);
         sep := h DIV 3;
         IF msg.y >= sep THEN newFocus := v.bottom.context(Context) ELSE newFocus := v.top.context(Context) END;
         focus := newFocus.view;
         IF (newFocus # v.focus) & ((msg IS Controllers.TrackMsg) OR (msg IS Controllers.DropMsg)) &
            SetFocus(focus, msg.x, msg.y) THEN
            (* remove marks in old focus *)
            mMsg.show := FALSE; mMsg.focus := TRUE;
            g := Views.ThisFrame(f, v.focus.view); IF g # NIL THEN Views.ForwardCtrlMsg(g, mMsg) END;
            v.focus := newFocus;   (* set new focus *)
            (* set marks in new focus *)
            mMsg.show := TRUE; mMsg.focus := TRUE;
            g := Views.ThisFrame(f, v.focus.view); IF g # NIL THEN Views.ForwardCtrlMsg(g, mMsg) END
         END
      (* the following scrolling-oriented messages are always sent to bottom view, independent of focus *)
      | msg: Controllers.PollSectionMsg DO
         g := Views.ThisFrame(f, v.bottom); IF g # NIL THEN Views.ForwardCtrlMsg(g, msg) END;
         IF msg.vertical & ~msg.done THEN
            msg.valid := FALSE; msg.done := TRUE   (* disable default-scrolling *)
         END
      | msg: Controllers.ScrollMsg DO
         g := Views.ThisFrame(f, v.bottom); IF g # NIL THEN Views.ForwardCtrlMsg(g, msg) END;
         IF msg.vertical THEN msg.done := TRUE END
      | msg: Controllers.PageMsg DO
         focus := v.bottom
      ELSE   (* all other messages are sent to the focus, however *)
         focus := v.focus.view
      END
      (* the assignment to focus signals that the view v wants to forward the message to the
      corresponding embedded view *)
   END HandleCtrlMsg;

   PROCEDURE (v: View) HandlePropMsg (VAR msg: Views.PropMessage);
      CONST defW = 80 * Ports.mm; defH = 60 * Ports.mm;
   BEGIN
      WITH msg: Properties.SizePref DO
         IF msg.w = Views.undefined THEN msg.w := defW END;
         IF msg.h = Views.undefined THEN msg.h := defH END
      | msg: Properties.ResizePref DO
         msg.verFitToWin := TRUE   (* make view always as high as window *)
      ELSE
         Views.HandlePropMsg(v.bottom, msg)
      END
   END HandlePropMsg;

   PROCEDURE (v: View) ConsiderFocusRequestBy (view: Views.View);
   BEGIN
      IF view = v.top THEN
         v.focus := v.top.context(Context)
      ELSIF view = v.bottom THEN
         v.focus := v.bottom.context(Context)
      ELSE
         HALT(20)
      END
   END ConsiderFocusRequestBy;

   PROCEDURE NewTwin* (top, bottom: Views.View): Views.View;
      VAR v: View;
   BEGIN
      NEW(v);
      v.top := NewContext(top, v); v.bottom := NewContext(bottom, v);
      NEW(v.ident); Stores.Join(v, v.ident); v.focus := v.bottom.context(Context);
      RETURN v
   END NewTwin;
```

вуаля: главный контейнер больше не хранит контексты, а получает их у nested views.

то есть, для кода он доступен (и с точки зрения кода, хранится) именно у nested. но нужен — parent. вот такая вот заковыка, которая может немножко сломать мозг.