How to сollect ALL items of the same type in the scene?

Hello again!
Help please solve the problem:

For example, I have a scene like this with UI:

How can I collect all TCastleRectangleControl by pascal code?

You mean the function with the TCastleUserInterface as argument and the array of TCastleComponent as Result, for example?

something like this:

var
  rootElement: TCastleUserInteface;
  elementsList: Array of TCastleRectangleControl;
begin
  rootElement:= DesignedComponent('SceneMain') as TCastleUserInteface;
...
{ do something, I don't know what
for filling elementsList }
...

elementsList; // filled
{ do any with this List }
end;

You can iterate, recursively, over the list of TCastleUserInterface children → to do any kind of searching.

This can be as simple as this: (adjusted code from ForceFallbackLook ):

procedure DoSomethingOnEachRectangle(const Ui: TCastleUserInterface);
var
  Child: TCastleUserInterface;
begin
  if Ui is TCastleRectangleControl then
  begin
    // do something on TCastleRectangleControl(Ui), or add it to a list
    TCastleRectangleControl(Ui).Xxx := ...;
  end;
  for Child in Ui do
    DoSomethingOnEachRectangle(Child);
end;

To iterate over all TCastleRectangleControl in a given view (whether created from code, or loaded from design) can use this with TCastleView instance as argument for DoSomethingOnEachRectangle . To iterate over absolutely everything visible now, you can pass all items from Window.Controls.

2 Likes

Ah, I see you mentioned rootElement and a list. So it would go like this:

type
  TCastleRectangleControlList = {$ifdef FPC}specialize{$endif} TObjectList<TCastleRectangleControl>;

procedure TMyView.Start;
var
  Rects: TCastleRectangleControlList;

  procedure EnumRects(const Ui: TCastleUserInterface);
  var
    Child: TCastleUserInterface;
  begin
    if Ui is TCastleRectangleControl then
      Rects.Add(TCastleRectangleControl(Ui));
    for Child in Ui do
      EnumRects(Child);
  end;

begin
  inherited;
  Rects := TCastleRectangleControlList.Create(false);
  try
    EnumRects(RootElement);
    { use Rects }
  finally 
    FreeAndNil(Rects);
  end;
end;
2 Likes

WHAT?! I can iterate UI elements with for…in cycle? Really? So easy?

Before I was try to use TCastleUserInterface.Component[i] as TComponent and always got some gibberish classes… for TCastleTransform it was fork fine…

OK I’ll try do it with for…in

{ 2min late }
O my Gosh! Its really working! Thanks!!!

1 Like

I’m happy my info helped :slight_smile:

As for the TCastleUserInterface.Component[I] – it will iterate over components “owned”. Which may, or may not, be what you’re looking for. See https://forum.castle-engine.io/t/cge-inside-delphi-for-beginner/ thread for my recent explanation how “ownership” differs from “visual hierarchy” in CGE (similar to LCL / VCL / FMX actually).

But, long story short, yeah, you want to iterate over visual hierarchy which means just for Child in Ui ... is what you need :slight_smile:

Phew! I figured out how to do it:

function GetAllUIImages(const rootItem: TCastleUserInterface): Array of TCastleImageControl;
type
  TItemsStack = {$ifdef FPC}specialize{$endif} TObjectStack<TCastleUserInterface>;
var
  item, child: TCastleUserInterface;
  items: TItemsStack;
begin
  Result:= [];
  items:= TItemsStack.Create(False);
  items.Push(rootItem);

  { iterate over all elements of tree }
  while (items.Count > 0) do
  begin
    item:= items.Pop;
    for child in item do
      items.Push(child);

    { pick up target }
    if (item is TCastleImageControl) then
      Insert((item as TCastleImageControl), Result, 0);
  end;

  FreeAndNil(items);
end;

For me main problem there was to understand which base class use to iterate on.

Although if I search for TCastleScenes in 3D objects, then I need to iterate over by the TComponent class not TCastleTransform, which is not obvious for me :crazy_face:
However, as I understand it, this is related with choice of “visual/unvisual hierarchy”.

PS: Ah yes, I’m already collect TCastleImageControl instead of TCastleRectangleControl, but solve is same. Tasks change over time.

1 Like

To find TCastleScene, knowing a TCastleViewport instance, one could do this:

procedure FindScenes(const ParentTransform: TCastleTransform);
var
  ChildTransform: TCastleTransform;
begin
  if ParentTransform is TCastleScene then 
  begin
    // ... do something with TCastleScene(ParentTransform)
  end;
  // iterate recursively
  for ChildTransform in ParentTransform do
    FindScenes(ChildTransform);
end;

FindScenes(MyViewport.Items);

If one doesn’t know MyViewport and/or wants to do this for all viewports – then first the code similar to previous posts can be used, i.e. TCastleViewport is another TCastleUserInterface hierarchy, so you can find all viewports within the UI hierarchy by iteration as shown in above posts.

This is more-or-less what the CGE editor is also doing to display the hierarchy on the left :slight_smile: So it’s really complete.