From 1951acdd95503cff0d78f714716164e104e3fac0 Mon Sep 17 00:00:00 2001
From: MARCHAND MANON <manonmarchand22@gmail.com>
Date: Thu, 19 Sep 2024 11:07:08 +0200
Subject: [PATCH] feat: make all aladin-lite init options work

---
 CHANGELOG.md            |   3 ++
 docs/conf.py            |   5 +-
 js/widget.js            |  16 +++---
 src/ipyaladin/widget.py | 114 ++++++++--------------------------------
 4 files changed, 37 insertions(+), 101 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index aaf7b364..79a50455 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Changed
 
 - Upgrade Aladin Lite version to 3.5.1-beta
+- instantiation options are now directly mirroring those of Aladin-Lite instead of being
+  hand-picked for the widget. Any option in
+  https://cds-astro.github.io/aladin-lite/global.html#AladinOptions will be accepted.
 
 ## [0.4.0]
 
diff --git a/docs/conf.py b/docs/conf.py
index b76bc28c..93eaa375 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -12,7 +12,7 @@
 with Path.open("../pyproject.toml", "rb") as config:
     toml = tomllib.load(config)
 import datetime
-from ipyaladin import __version__, Aladin
+from ipyaladin import __version__
 
 project = toml["project"]["name"]
 author = "Strasbourg Astronomical Date Centre (CDS)"
@@ -90,6 +90,7 @@
 
 # -- Document Init Options ---------------------------------------------------
 
+"""" # commented out since init_options are now given directly to aladin lite
 init_options = Aladin().traits(only_init=True)
 
 with Path.open("user_documentation/init_options.csv", "w"):
@@ -98,7 +99,7 @@
 with Path.open("user_documentation/init_options.csv", "a") as f:
     for k, v in init_options.items():
         f.write(f"{k};{v.default_value};{v.help}\n")
-
+"""
 
 # -- Options for HTML output -------------------------------------------------
 # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
diff --git a/js/widget.js b/js/widget.js
index 2cf2b535..92a0a4d2 100644
--- a/js/widget.js
+++ b/js/widget.js
@@ -5,14 +5,15 @@ import A from "./aladin_lite";
 
 function initAladinLite(model, el) {
   setDivNumber(divNumber + 1);
+  let initFromPython = model.get("_init_options");
   let initOptions = {};
-  model.get("init_options").forEach((option_name) => {
-    initOptions[snakeCaseToCamelCase(option_name)] = model.get(option_name);
-  });
+  for (const key in initFromPython) {
+    initOptions[snakeCaseToCamelCase(key)] = initFromPython[key];
+  }
 
   let aladinDiv = document.createElement("div");
   aladinDiv.classList.add("aladin-widget");
-  aladinDiv.style.height = `${initOptions["height"]}px`;
+  aladinDiv.style.height = `${model.get("_height")}px`;
 
   aladinDiv.id = `aladin-lite-div-${divNumber}`;
   let aladin = new A.aladin(aladinDiv, initOptions);
@@ -20,16 +21,17 @@ function initAladinLite(model, el) {
 
   // Set the target again after the initialization to be sure that the target is set
   // from icrs coordinates because of the use of gotoObject in the Aladin Lite API
-  const raDec = initOptions["target"].split(" ");
+  const raDec = model.get("_target").split(" ");
   aladin.gotoRaDec(raDec[0], raDec[1]);
 
   // Set current FoV and WCS
-  const twoAxisFoV = aladin.getFov();
+  const twoAxisFoV = { ...aladin.getFov() };
   model.set("_fov_xy", {
     x: twoAxisFoV[0],
     y: twoAxisFoV[1],
   });
-  model.set("_wcs", aladin.getViewWCS());
+  const wcs = { ...aladin.getViewWCS() };
+  model.set("_wcs", wcs);
   model.set("_is_loaded", true);
   model.save_changes();
 
diff --git a/src/ipyaladin/widget.py b/src/ipyaladin/widget.py
index 5a4c342b..a6974e11 100644
--- a/src/ipyaladin/widget.py
+++ b/src/ipyaladin/widget.py
@@ -53,7 +53,6 @@
     Unicode,
     Bool,
     Any,
-    default,
 )
 
 SupportedRegion = Union[
@@ -131,6 +130,7 @@ class Aladin(anywidget.AnyWidget):
     _css: Final = pathlib.Path(__file__).parent / "static" / "widget.css"
 
     # Options for the view initialization
+    _init_options = traitlets.Dict().tag(sync=True)
     _height = Int(400).tag(sync=True, init_option=True)
     _target = Unicode(
         "0 0",
@@ -153,91 +153,12 @@ class Aladin(anywidget.AnyWidget):
     )
     coo_frame = Unicode(
         "ICRS",
-        help="The frame coordinate. Can be either 'ICRS', 'ICRSd', or 'Galactic'.",
+        help="The frame coordinate. Can be either 'ICRS', 'ICRSd', or 'galactic'.",
     ).tag(sync=True, init_option=True)
     projection = Unicode(
         "SIN",
         help="The projection for the view. The keywords follow the FITS standard.",
     ).tag(sync=True, init_option=True)
-    samp = Bool(False, help="Wether to allow sending data via the SAMP protocol.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    # Buttons on/off
-    background_color = Unicode(
-        "rgb(60, 60, 60)", help="The color behind the surveys in RGB format."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_zoom_control = Bool(
-        False, help="Whether to show the zoom control toolbar."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_layers_control = Bool(
-        True, help="Whether to show the layers control button."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_fullscreen_control = Bool(
-        True, help="Whether to show the fullscreen control toolbar."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_simbad_pointer_control = Bool(
-        True, help="Whether to show the Simbad pointer control toolbar."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_settings_control = Bool(
-        True, help="Whether to show the settings control toolbar."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_share_control = Bool(
-        False, help="Whether to show the share control toolbar."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_status_bar = Bool(True, help="Whether to show the status bar.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    show_frame = Bool(True, help="Whether to show the viewport frame.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    show_fov = Bool(True, help="Whether to show the field of view indicator.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    show_coo_location = Bool(True, help="Whether to show the coordinates bar.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    show_projection_control = Bool(
-        True, help="Whether to show the coordinate location indicator."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_context_menu = Bool(
-        True, help="Whether the right click should start the contextual menu."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_catalog = Bool(True, help="Whether to show the catalog.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    full_screen = Bool(False, help="Whether to start in full-screen mode.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    # reticle
-    show_reticle = Bool(
-        True, help="Whether to show the reticle in the middle of the view."
-    ).tag(sync=True, init_option=True, only_init=True)
-    reticle_color = Unicode("rgb(178, 50, 178)", help="The color of the reticle.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    reticle_size = Int(20, help="Whether to show the reticle in the middle.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-    # grid
-    show_coo_grid = Bool(
-        False, help="Whether the coordinates grid should be shown at startup."
-    ).tag(sync=True, init_option=True, only_init=True)
-    show_coo_grid_control = Bool(
-        True, help="Whether to show the coordinate grid control toolbar."
-    ).tag(sync=True, init_option=True, only_init=True)
-    grid_color = Unicode(
-        "rgb(178, 50, 178)",
-        help="Color of the grid. Can be specified as a named color "
-        "(see html named colors), as rgb (ex: 'rgb(178, 50, 178)'), "
-        "or as a hex color (ex: '#86D6AE').",
-    ).tag(sync=True, init_option=True, only_init=True)
-    grid_opacity = Float(
-        0.5, help="Opacity of the grid and labels. It is comprised between 0 and 1."
-    ).tag(sync=True, init_option=True, only_init=True)
-    grid_options = traitlets.Dict(help="More options for the grid.").tag(
-        sync=True, init_option=True, only_init=True
-    )
-
     # Values
     _ready = Bool(
         False,
@@ -282,17 +203,26 @@ class Aladin(anywidget.AnyWidget):
         "is reduced in size when hidden.",
     ).tag(sync=True)
 
-    init_options = traitlets.List(trait=traitlets.Any()).tag(sync=True)
-
-    @default("init_options")
-    def _init_options(self) -> List[str]:
-        return list(self.traits(init_option=True))
-
-    def __init__(self, *args: any, **kwargs: any) -> None:
-        super().__init__(*args, **kwargs)
-        self.height = kwargs.get("height", 400)
-        self.target = kwargs.get("target", "0 0")
-        self.fov = kwargs.get("fov", 60.0)
+    def __init__(self, *args: any, **init_options: any) -> None:
+        super().__init__(*args, **init_options)
+        # pop init options of ipywidgets.DOMWidget that would choke ipyaladin
+        # https://github.com/jupyter-widgets/ipywidgets/blob/main/python/ipywidgets/ipywidgets/widgets/domwidget.py
+        for key in ["layout", "tabbable", "tooltip"]:
+            init_options.pop(key, None)
+        # some init options are properties here
+        self.height = init_options.get("height", self._height)
+        self.target = init_options.get("target", self._target)
+        self.fov = init_options.get("fov", self._fov)
+        # apply different default options from Aladin-Lite
+        ipyaladin_default = {
+            "show_simbad_pointer_control": True,
+            "show_coo_grid_control": True,
+            "show_settings_control": True,
+            "show_context_menu": True,
+        }
+        init_options = {**ipyaladin_default, **init_options}
+        # set the traitlet
+        self._init_options = init_options
         self.on_msg(self._handle_custom_message)
 
     def _handle_custom_message(self, _: any, message: dict, buffers: any) -> None: