magscope.ui.panel_layout
========================

.. py:module:: magscope.ui.panel_layout

.. autoapi-nested-parse::

   Shared helpers for draggable control panel layouts.



Attributes
----------

.. autoapisummary::

   magscope.ui.panel_layout.PANEL_MIME_TYPE


Classes
-------

.. autoapisummary::

   magscope.ui.panel_layout._TitleDragFilter
   magscope.ui.panel_layout.PanelWrapper
   magscope.ui.panel_layout.ReorderableColumn
   magscope.ui.panel_layout.PanelLayoutManager


Module Contents
---------------

.. py:data:: PANEL_MIME_TYPE
   :value: 'application/x-magscope-panel'


.. py:class:: _TitleDragFilter(wrapper: PanelWrapper, target: PyQt6.QtWidgets.QWidget)

   Bases: :py:obj:`PyQt6.QtCore.QObject`


   Convert title-area drags into wrapper move operations.


   .. py:attribute:: _wrapper


   .. py:attribute:: target


   .. py:attribute:: _drag_start


   .. py:attribute:: _dragging
      :value: False



   .. py:method:: eventFilter(obj, event)


   .. py:method:: drag_finished() -> None


.. py:class:: PanelWrapper(manager: PanelLayoutManager, panel_id: str, widget: PyQt6.QtWidgets.QWidget, *, draggable: bool = True)

   Bases: :py:obj:`PyQt6.QtWidgets.QFrame`


   Wrap a panel widget and make its title initiate drag-and-drop.


   .. py:attribute:: _manager


   .. py:attribute:: panel_id


   .. py:attribute:: panel_widget


   .. py:attribute:: column
      :type:  ReorderableColumn | None
      :value: None



   .. py:attribute:: _drag_filters
      :type:  list[_TitleDragFilter]
      :value: []



   .. py:attribute:: draggable
      :value: True



   .. py:attribute:: _drop_accepted
      :value: False



   .. py:method:: _attach_title_drag() -> None


   .. py:method:: _register_drag_source(widget: PyQt6.QtWidgets.QWidget | None) -> None


   .. py:method:: start_drag() -> None


   .. py:method:: mark_drop_accepted() -> None


.. py:class:: ReorderableColumn(name: str, pinned_ids: Iterable[str] | None = None)

   Bases: :py:obj:`PyQt6.QtWidgets.QWidget`


   Vertical column of draggable panels with drop support.


   .. py:attribute:: name


   .. py:attribute:: _layout


   .. py:attribute:: _placeholder
      :type:  PyQt6.QtWidgets.QFrame | None
      :value: None



   .. py:attribute:: _pinned_ids


   .. py:attribute:: _active_drag_height
      :type:  int | None
      :value: None



   .. py:attribute:: _manager
      :type:  PanelLayoutManager | None
      :value: None



   .. py:method:: set_manager(manager: PanelLayoutManager | None) -> None


   .. py:method:: panels() -> list[PanelWrapper]


   .. py:method:: panel_ids() -> list[str]


   .. py:method:: add_panel(wrapper: PanelWrapper, index: int | None = None) -> None


   .. py:method:: remove_panel(wrapper: PanelWrapper) -> None


   .. py:method:: clear_panels() -> None


   .. py:method:: begin_drag(wrapper: PanelWrapper) -> int


   .. py:method:: cancel_drag(wrapper: PanelWrapper, index: int) -> None


   .. py:method:: finish_drag() -> None


   .. py:method:: _target_index(index: int | None) -> int


   .. py:method:: _drop_index(cursor_y: float) -> int


   .. py:method:: _locked_prefix_length() -> int


   .. py:method:: _constrain_index(wrapper: PanelWrapper, index: int) -> int


   .. py:method:: _constrained_drop_index(wrapper: PanelWrapper, cursor_y: float) -> int


   .. py:method:: _ensure_placeholder() -> PyQt6.QtWidgets.QFrame


   .. py:method:: _update_placeholder(wrapper: PanelWrapper | None, cursor_y: float) -> None


   .. py:method:: clear_placeholder() -> None


   .. py:method:: _placeholder_index() -> int | None


   .. py:method:: dragEnterEvent(event) -> None


   .. py:method:: dragMoveEvent(event) -> None


   .. py:method:: dragLeaveEvent(event) -> None


   .. py:method:: dropEvent(event) -> None


   .. py:method:: _wrapper_from_event(event) -> PanelWrapper | None


.. py:class:: PanelLayoutManager(settings: QSettings | None, settings_group: str, columns: dict[str, ReorderableColumn] | Iterable[tuple[str, ReorderableColumn]], *, on_layout_changed: Callable[[dict[str, list[str]]], None] | None = None, on_drag_active_changed: Callable[[bool], None] | None = None)

   Coordinate draggable panel columns and persist their layout.


   .. py:attribute:: _settings
      :type:  PyQt6.QtCore.QSettings | None


   .. py:attribute:: _settings_group


   .. py:attribute:: _wrappers
      :type:  dict[str, PanelWrapper]


   .. py:attribute:: _default_columns
      :type:  dict[str, str]


   .. py:attribute:: _default_order
      :type:  list[str]
      :value: []



   .. py:attribute:: _on_layout_changed
      :value: None



   .. py:attribute:: _on_drag_active_changed
      :value: None



   .. py:attribute:: _active_drag_count
      :value: 0



   .. py:method:: wrapper_for_id(panel_id: str) -> PanelWrapper | None


   .. py:method:: register_panel(panel_id: str, widget: PyQt6.QtWidgets.QWidget, default_column: str, *, draggable: bool = True) -> PanelWrapper


   .. py:method:: restore_layout() -> None


   .. py:method:: save_layout() -> None


   .. py:method:: current_layout() -> dict[str, list[str]]


   .. py:method:: layout_changed() -> None


   .. py:method:: notify_drag_started() -> None


   .. py:method:: notify_drag_finished() -> None


   .. py:method:: _normalise_panel_list(stored) -> list[str] | None


   .. py:method:: _load_layout() -> OrderedDict[str, list[str]]


   .. py:method:: stored_layout() -> OrderedDict[str, list[str]]


   .. py:method:: stored_column_names() -> list[str]


   .. py:method:: add_column(name: str, column: ReorderableColumn, index: int | None = None) -> None


   .. py:method:: remove_column(name: str) -> None


