diff --git a/src/calng/scenes.py b/src/calng/scenes.py
index 35273dd41f49c90c6dd0ccedc01f7e483911f64d..c39d584fb6fa5991403689f1b38949a43f94f2b7 100644
--- a/src/calng/scenes.py
+++ b/src/calng/scenes.py
@@ -1,5 +1,4 @@
 import enum
-import textwrap
 
 import karathon
 from karabo.common.scenemodel.api import (
@@ -17,11 +16,13 @@ from karabo.common.scenemodel.api import (
     LineEditModel,
     RectangleModel,
     SceneModel,
+    SceneTargetWindow,
     write_scene,
 )
 
-BASE_INC = 30
-PADDING = 10
+BASE_INC = 25
+NARROW_INC = 20
+PADDING = 5
 RECONFIGURABLE = 4  # TODO: look up proper enum
 
 _type_to_line_editable = {
@@ -185,287 +186,197 @@ class VerticalLayout:
         )
 
 
-class MaybeEditableRow:
+class MaybeEditableRow(HorizontalLayout):
     def __init__(
         self,
         device_id,
         schema_hash,
         key_path,
-        label_width=7,
-        display_width=5,
-        edit_width=5,
+        label_width=7 * NARROW_INC,
+        display_width=5 * NARROW_INC,
+        edit_width=5 * NARROW_INC,
+        height=NARROW_INC,
     ):
-        self.label_width = label_width * BASE_INC
-        self.display_width = display_width * BASE_INC
-        self.edit_width = edit_width * BASE_INC
-
+        super().__init__(padding=0)
         key_attr = schema_hash.getAttributes(key_path)
-        label_text = textwrap.shorten(
+        label_text = (
             key_attr["displayedName"]
             if "displayedName" in key_attr
-            else key_path.split(".")[-1],
-            24,
-        )
-
-        self.label = LabelModel(
-            text=label_text,
-            width=self.label_width,
-            height=BASE_INC,
+            else key_path.split(".")[-1]
         )
-        self.value_display = DisplayLabelModel(
-            keys=[f"{device_id}.{key_path}"],
-            width=self.display_width,
-            height=BASE_INC,
+        self.children.extend(
+            [
+                LabelModel(
+                    text=label_text,
+                    width=label_width,
+                    height=height,
+                ),
+                DisplayLabelModel(
+                    keys=[f"{device_id}.{key_path}"],
+                    width=display_width,
+                    height=height,
+                ),
+            ]
         )
         if key_attr["accessMode"] == RECONFIGURABLE:
             value_type = key_attr["valueType"]
             if value_type in _type_to_line_editable:
                 line_editable_class, extra_args = _type_to_line_editable[value_type]
-                self.value_edit = line_editable_class(
-                    keys=[f"{device_id}.{key_path}"],
-                    width=self.edit_width,
-                    height=BASE_INC,
-                    **extra_args,
+                self.children.append(
+                    line_editable_class(
+                        keys=[f"{device_id}.{key_path}"],
+                        width=edit_width,
+                        height=height,
+                        **extra_args,
+                    )
                 )
             else:
-                self.value_edit = LabelModel(
-                    text=f"Not yet supported: editing {key_path} of type {value_type}",
-                    width=self.edit_width,
-                    height=BASE_INC,
+                self.children.append(
+                    value_edit=LabelModel(
+                        text=f"Not yet supported: editing {key_path} of type {value_type}",
+                        width=edit_width,
+                        height=height,
+                    )
                 )
 
-    @property
-    def width(self):
-        return self.label_width + self.display_width + self.edit_width
-
-    @property
-    def height(self):
-        return BASE_INC
-
-    def render(self, x, y):
-        self.label.x = x
-        self.label.y = y
-        self.value_display.x = x + self.label_width
-        self.value_display.y = y
-        if hasattr(self, "value_edit"):
-            self.value_edit.x = x + self.label_width + self.display_width
-            self.value_edit.y = y
-            return [
-                self.label,
-                self.value_display,
-                self.value_edit,
-            ]
-        else:
-            return [
-                self.label,
-                self.value_display,
-            ]
-
 
 @titled("Parameters used for CalCat queries", width=10)
 @boxed
-class ConstantParameterColumn:
+class ConstantParameterColumn(VerticalLayout):
     def __init__(self, device_id, schema_hash, prefix="constantParameters"):
+        super().__init__(padding=0)
         if "." in prefix:
-            self.extra_path_prefix = prefix[: prefix.rfind(".") + 1]
+            extra_path_prefix = prefix[: prefix.rfind(".") + 1]
         else:
-            self.extra_path_prefix = ""
-        device_id = device_id
-        self.rows = [
-            MaybeEditableRow(
-                device_id,
-                schema_hash,
-                f"{prefix}.{key}",
+            extra_path_prefix = ""
+        self.children.extend(
+            [
+                MaybeEditableRow(
+                    device_id,
+                    schema_hash,
+                    f"{prefix}.{key}",
+                )
+                for key in schema_hash.get(prefix).getKeys()
+            ]
+        )
+        self.children.append(
+            DisplayCommandModel(
+                keys=[f"{device_id}.{extra_path_prefix}loadMostRecentConstants"],
+                width=10 * BASE_INC,
+                height=BASE_INC,
             )
-            for key in schema_hash.get(prefix).getKeys()
-        ]
-        self.load_button = DisplayCommandModel(
-            keys=[f"{device_id}.{self.extra_path_prefix}loadMostRecentConstants"],
-            width=10 * BASE_INC,
-            height=BASE_INC,
         )
 
-    def render(self, x, y):
-        res = []
-        y_offset = 0
-        for row in self.rows:
-            res.extend(row.render(x=x, y=y + y_offset))
-            y_offset += row.height
-        self.load_button.x = x + 7 * BASE_INC
-        self.load_button.y = y + y_offset
-        res.append(self.load_button)
-
-        return res
-
-    @property
-    def width(self):
-        return max(row.width for row in self.rows)
-
-    @property
-    def height(self):
-        return sum(row.height for row in self.rows) + BASE_INC
-
 
-class ConstantNode:
+class ConstantNode(HorizontalLayout):
     def __init__(self, device_id, constant_path):
-        self.title = LabelModel(
-            text=constant_path.split(".")[-1],
-            width=7 * BASE_INC,
-            height=BASE_INC,
-        )
-        self.ampel = ColorBoolModel(
-            height=BASE_INC, width=BASE_INC, keys=[f"{device_id}.{constant_path}.found"]
+        super().__init__(padding=0)
+        self.children.extend(
+            [
+                LabelModel(
+                    text=constant_path.split(".")[-1],
+                    width=7 * NARROW_INC,
+                    height=NARROW_INC,
+                ),
+                ColorBoolModel(
+                    width=NARROW_INC,
+                    height=NARROW_INC,
+                    keys=[f"{device_id}.{constant_path}.found"],
+                ),
+            ]
         )
 
-    def render(self, x, y):
-        self.title.x = x
-        self.title.y = y
-        self.ampel.x = x + 7 * BASE_INC
-        self.ampel.y = y
-        y += BASE_INC
-        return [self.title, self.ampel]
-
-    @property
-    def width(self):
-        return 8 * BASE_INC
-
-    @property
-    def height(self):
-        return BASE_INC
-
 
 @titled("Found constants", width=6)
 @boxed
-class FoundConstantsColumn:
+class FoundConstantsColumn(VerticalLayout):
     def __init__(self, device_id, schema_hash, prefix="foundConstants"):
-        self.rows = [
-            ConstantNode(device_id, f"{prefix}.{constant_name}")
-            for constant_name in schema_hash.get(prefix).getKeys()
-        ]
+        super().__init__(padding=0)
+        self.children.extend(
+            [
+                ConstantNode(device_id, f"{prefix}.{constant_name}")
+                for constant_name in schema_hash.get(prefix).getKeys()
+            ]
+        )
 
-    def render(self, x, y):
-        res = []
-        y_offset = 0
-        for row in self.rows:
-            res.extend(row.render(x, y + y_offset))
-            y_offset += row.height
-        return res
 
-    @property
-    def width(self):
-        return max(row.width for row in self.rows)
+class Space:
+    def __init__(self, width, height):
+        self.width = width
+        self.height = height
 
-    @property
-    def height(self):
-        return sum(row.height for row in self.rows)
+    def render(self, x, y):
+        return []
 
 
-class CorrectionStepNode:
+class CorrectionStepNode(HorizontalLayout):
     def __init__(self, device_id, step_path):
-        self.label = LabelModel(
-            text=step_path.split(".")[-1], width=7 * BASE_INC, height=BASE_INC
-        )
-        self.checkbox_main = CheckBoxModel(
-            keys=[f"{device_id}.{step_path}.enable"],
-            width=BASE_INC,
-            height=BASE_INC,
-            klass="EditableCheckBox",
-        )
-        self.checkbox_preview = CheckBoxModel(
-            keys=[f"{device_id}.{step_path}.preview"],
-            width=BASE_INC,
-            height=BASE_INC,
-            klass="EditableCheckBox",
+        super().__init__(padding=0)
+        self.children.extend(
+            [
+                LabelModel(
+                    text=step_path.split(".")[-1], width=7 * BASE_INC, height=BASE_INC
+                ),
+                CheckBoxModel(
+                    keys=[f"{device_id}.{step_path}.enable"],
+                    width=BASE_INC,
+                    height=BASE_INC,
+                    klass="EditableCheckBox",
+                ),
+                CheckBoxModel(
+                    keys=[f"{device_id}.{step_path}.preview"],
+                    width=BASE_INC,
+                    height=BASE_INC,
+                    klass="EditableCheckBox",
+                ),
+            ]
         )
 
-    def render(self, x, y):
-        self.label.x = x
-        self.label.y = y
-        x += 5 * BASE_INC
-        self.checkbox_main.x = x
-        self.checkbox_main.y = y
-        x += 3 * BASE_INC
-        self.checkbox_preview.x = x
-        self.checkbox_preview.y = y
-        return [self.label, self.checkbox_main, self.checkbox_preview]
-
-    @property
-    def width(self):
-        return 10 * BASE_INC
-
-    @property
-    def height(self):
-        return BASE_INC
-
 
 @titled("Correction steps", width=6)
 @boxed
-class CorrectionStepsColumn:
+class CorrectionStepsColumn(VerticalLayout):
     def __init__(self, device_id, schema_hash, prefix="corrections"):
-        self.header_main = LabelModel(
-            text="enable", width=3 * BASE_INC, height=BASE_INC
+        super().__init__(padding=0)
+        self.children.append(
+            HorizontalLayout(
+                children=[
+                    Space(width=7 * BASE_INC, height=BASE_INC),
+                    LabelModel(
+                        text="enable/preview", width=6 * BASE_INC, height=BASE_INC
+                    ),
+                ],
+                padding=0,
+            )
         )
-        self.header_preview = LabelModel(
-            text="preview", width=3 * BASE_INC, height=BASE_INC
+        self.children.extend(
+            [
+                CorrectionStepNode(device_id, f"{prefix}.{step_path}")
+                for step_path in schema_hash.get(prefix).getKeys()
+            ]
         )
-        self.steps = [
-            CorrectionStepNode(device_id, f"{prefix}.{step_path}")
-            for step_path in schema_hash.get(prefix).getKeys()
-        ]
-
-    def render(self, x, y):
-        self.header_main.x = x + 5 * BASE_INC
-        self.header_main.y = y
-        self.header_preview.x = x + 8 * BASE_INC
-        self.header_preview.y = y
-        res = [self.header_main, self.header_preview]
-        y += BASE_INC
-        for step in self.steps:
-            step.x = x
-            step.y = y
-            res.extend(step.render(x, y))
-            y += step.height
-        return res
-
-    @property
-    def width(self):
-        return max(step.width for step in self.steps)
-
-    @property
-    def height(self):
-        return sum(step.height for step in self.steps) + BASE_INC
 
 
-class ConstantLoadedAmpeln:
+class ConstantLoadedAmpeln(HorizontalLayout):
     def __init__(self, device_id, schema_hash, prefix="foundConstants"):
-        self.content = HorizontalLayout(
-            children=[
+        super().__init__(padding=0)
+        self.children.extend(
+            [
                 ColorBoolModel(
                     keys=[f"{device_id}.{prefix}.{key}.found"],
                     height=BASE_INC,
                     width=BASE_INC,
                 )
                 for key in schema_hash.get(prefix).getKeys()
-            ],
-            padding=0,
+            ]
         )
 
-    def render(self, x, y):
-        return self.content.render(x, y)
-
-    @property
-    def width(self):
-        return self.content.width
-
-    @property
-    def height(self):
-        return self.content.height
-
 
 @titled("Manager status", width=6)
 @boxed
-class ManagerDeviceStatus:
+class ManagerDeviceStatus(VerticalLayout):
     def __init__(self, device_id):
+        super().__init__(padding=0)
         self.name = DisplayLabelModel(
             keys=[f"{device_id}.deviceId"],
             width=14 * BASE_INC,
@@ -497,8 +408,8 @@ class ManagerDeviceStatus:
             width=14 * BASE_INC,
             height=7 * BASE_INC,
         )
-        self.content = VerticalLayout(
-            children=[
+        self.children.extend(
+            [
                 self.name,
                 HorizontalLayout(
                     children=[
@@ -512,26 +423,15 @@ class ManagerDeviceStatus:
                     padding=0,
                 ),
                 self.status_log,
-            ],
-            padding=0,
+            ]
         )
 
-    def render(self, x, y):
-        return self.content.render(x, y)
-
-    @property
-    def width(self):
-        return self.content.width
-
-    @property
-    def height(self):
-        return self.content.height
-
 
 @titled("Device status", width=6)
 @boxed
-class CorrectionDeviceStatus:
+class CorrectionDeviceStatus(VerticalLayout):
     def __init__(self, device_id):
+        super().__init__(padding=0)
         self.name = DisplayLabelModel(
             keys=[f"{device_id}.deviceId"],
             width=14 * BASE_INC,
@@ -563,10 +463,10 @@ class CorrectionDeviceStatus:
         self.status_log = DisplayTextLogModel(
             keys=[f"{device_id}.status"],
             width=14 * BASE_INC,
-            height=20 * BASE_INC,
+            height=14 * BASE_INC,
         )
-        self.content = VerticalLayout(
-            children=[
+        self.children.extend(
+            [
                 self.name,
                 HorizontalLayout(
                     children=[
@@ -583,71 +483,44 @@ class CorrectionDeviceStatus:
                     padding=0,
                 ),
                 self.status_log,
-            ],
-            padding=0,
+            ]
         )
 
-    def render(self, x, y):
-        return self.content.render(x, y)
-
-    @property
-    def width(self):
-        return 14 * BASE_INC
 
-    @property
-    def height(self):
-        return 23 * BASE_INC
-
-
-class CompactCorrectionDeviceOverview:
+class CompactCorrectionDeviceOverview(HorizontalLayout):
     def __init__(self, device_id, schema_hash):
-        self.name = DeviceSceneLinkModel(
-            text=device_id.split("/")[-1],
-            keys=[f"{device_id}.availableScenes"],
-            target="overview",
-            width=5 * BASE_INC,
-            height=BASE_INC,
-        )
-        self.status = DisplayStateColorModel(
-            show_string=True,
-            keys=[f"{device_id}.state"],
-            width=6 * BASE_INC,
-            height=BASE_INC,
-        )
-        self.rate = EvaluatorModel(
-            expression="f'{x:.02f}'",
-            keys=[f"{device_id}.performance.rate"],
-            width=4 * BASE_INC,
-            height=BASE_INC,
-        )
-        self.tid = DisplayLabelModel(
-            keys=[f"{device_id}.trainId"],
-            width=4 * BASE_INC,
-            height=BASE_INC,
-        )
-        self.ampeln = ConstantLoadedAmpeln(device_id, schema_hash)
-        self.content = HorizontalLayout(
-            children=[
-                self.name,
-                self.status,
-                self.rate,
-                self.tid,
-                self.ampeln,
-            ],
-            padding=0,
+        super().__init__(padding=0)
+        self.children.extend(
+            [
+                DeviceSceneLinkModel(
+                    text=device_id.split("/")[-1],
+                    keys=[f"{device_id}.availableScenes"],
+                    target="overview",
+                    target_window=SceneTargetWindow.Dialog,
+                    width=5 * BASE_INC,
+                    height=BASE_INC,
+                ),
+                DisplayStateColorModel(
+                    show_string=True,
+                    keys=[f"{device_id}.state"],
+                    width=6 * BASE_INC,
+                    height=BASE_INC,
+                ),
+                EvaluatorModel(
+                    expression="f'{x:.02f}'",
+                    keys=[f"{device_id}.performance.rate"],
+                    width=4 * BASE_INC,
+                    height=BASE_INC,
+                ),
+                DisplayLabelModel(
+                    keys=[f"{device_id}.trainId"],
+                    width=4 * BASE_INC,
+                    height=BASE_INC,
+                ),
+                ConstantLoadedAmpeln(device_id, schema_hash),
+            ]
         )
 
-    def render(self, x, y):
-        return self.content.render(x, y)
-
-    @property
-    def width(self):
-        return 19 * BASE_INC + self.ampeln.width
-
-    @property
-    def height(self):
-        return BASE_INC
-
 
 def correction_device_overview_scene(device_id, schema):
     if isinstance(schema, karathon.Schema):