From 7c361a0fa9c3303e3e05de40480089c399dad544 Mon Sep 17 00:00:00 2001 From: hahmann Date: Wed, 7 Oct 2026 14:01:30 +0200 Subject: [PATCH 01/25] Add logo spots and spot segmentation test datasets for BDV overlay Two 2D OME-Zarr v0.5 images with identical extents and 2 levels, to test adding a segmentation to an already open BigDataViewer (#127): spots laid out like the repository logo next to the Zarr wordmark, and one label per spot and letter. OMERO metadata colors them green and magenta, showing every label. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../ome/zarr/testdata/bdv_testing/README.md | 45 ++++ .../ome/zarr/testdata/bdv_testing/conda.yml | 11 + .../testdata/bdv_testing/create_logo_spots.py | 200 ++++++++++++++++++ .../bdv_testing/logo_labels.ome.zarr/s0/c/0/0 | Bin 0 -> 780 bytes .../bdv_testing/logo_labels.ome.zarr/s0/c/0/1 | Bin 0 -> 1173 bytes .../bdv_testing/logo_labels.ome.zarr/s0/c/1/0 | Bin 0 -> 744 bytes .../bdv_testing/logo_labels.ome.zarr/s0/c/1/1 | Bin 0 -> 596 bytes .../logo_labels.ome.zarr/s0/zarr.json | 46 ++++ .../bdv_testing/logo_labels.ome.zarr/s1/c/0/0 | Bin 0 -> 1485 bytes .../logo_labels.ome.zarr/s1/zarr.json | 46 ++++ .../logo_labels.ome.zarr/zarr.json | 68 ++++++ .../bdv_testing/logo_spots.ome.zarr/s0/c/0/0 | Bin 0 -> 51028 bytes .../bdv_testing/logo_spots.ome.zarr/s0/c/0/1 | Bin 0 -> 51819 bytes .../bdv_testing/logo_spots.ome.zarr/s0/c/1/0 | Bin 0 -> 50873 bytes .../bdv_testing/logo_spots.ome.zarr/s0/c/1/1 | Bin 0 -> 48229 bytes .../logo_spots.ome.zarr/s0/zarr.json | 43 ++++ .../bdv_testing/logo_spots.ome.zarr/s1/c/0/0 | Bin 0 -> 50591 bytes .../logo_spots.ome.zarr/s1/zarr.json | 43 ++++ .../bdv_testing/logo_spots.ome.zarr/zarr.json | 68 ++++++ 19 files changed, 570 insertions(+) create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/README.md create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/conda.yml create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/create_logo_spots.py create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/0/0 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/0/1 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/1/0 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/1/1 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/zarr.json create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s1/c/0/0 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s1/zarr.json create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/zarr.json create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/c/0/0 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/c/0/1 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/c/1/0 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/c/1/1 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/zarr.json create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s1/c/0/0 create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s1/zarr.json create mode 100644 test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/zarr.json diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/README.md b/test-shared/resources/ome/zarr/testdata/bdv_testing/README.md new file mode 100644 index 000000000..f39dba6be --- /dev/null +++ b/test-shared/resources/ome/zarr/testdata/bdv_testing/README.md @@ -0,0 +1,45 @@ +# Logo Spots and Spot Segmentation Test OME-Zarr Datasets + +Two related 2D datasets (OME-Zarr v0.5) with identical extents, so that they overlay exactly: + +* `logo_spots.ome.zarr` → intensity image (uint8): 23 Gaussian spots laid out like the cubes of this repository's logo, + next to the Zarr wordmark in the same blurred style, on a noisy background +* `logo_labels.ome.zarr` → segmentation (uint16): one label id per spot (1–23) and per letter of "zarr" + (24–27), 0 is background + +They are deliberately not called "blobs", so they do not get mixed up with Fiji's well-known *Blobs* sample image. + +Each dataset stores: + +* A single 2D image (Y × X), 512 × 512 pixels +* 2 resolution levels (multiscale pyramid, factor 2) + +The second level is made by subsampling, not averaging, so the label ids stay valid. + +Both carry OMERO metadata with a channel color and contrast limits: the spots are green with a window of 20–230, the +labels magenta with a window of 0–1, so every label is shown at full brightness right after opening. + +## Purpose + +These datasets are designed for testing the display of two OME-Zarr images in the same viewer, e.g. adding a +segmentation to an already open BigDataViewer (issue #127). + +## How to Reproduce + +To recreate the datasets, first create a Conda environment using the provided `conda.yml`: + +```bash +conda env create -f conda.yml +conda activate ome-zarr-test +``` + +Then, run the dataset creation script: + +```bash +python create_logo_spots.py +``` + +It reads the Zarr wordmark from `doc/logo/zarr-pink-horizontal.svg` in this repository and generates: + +* `logo_spots.ome.zarr` +* `logo_labels.ome.zarr` diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/conda.yml b/test-shared/resources/ome/zarr/testdata/bdv_testing/conda.yml new file mode 100644 index 000000000..d2a5b5820 --- /dev/null +++ b/test-shared/resources/ome/zarr/testdata/bdv_testing/conda.yml @@ -0,0 +1,11 @@ +name: ome-zarr-test +channels: + - conda-forge +dependencies: + - python=3.11 + - numpy + - zarr + - ome-zarr + - cairosvg + - pillow + - pip diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/create_logo_spots.py b/test-shared/resources/ome/zarr/testdata/bdv_testing/create_logo_spots.py new file mode 100644 index 000000000..3c45e0731 --- /dev/null +++ b/test-shared/resources/ome/zarr/testdata/bdv_testing/create_logo_spots.py @@ -0,0 +1,200 @@ +### +# #%L +# OME-Zarr extras for Fiji +# %% +# Copyright (C) 2022 - 2026 SciJava developers +# %% +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# #L% +### +import io +import pathlib + +import cairosvg +import numpy as np +import zarr +from PIL import Image, ImageDraw +from ome_zarr.writer import write_multiscale +from ome_zarr.io import parse_url +from ome_zarr.format import FormatV05 + +# The Zarr logo the repository logo uses, see doc/logo +ZARR_SVG = pathlib.Path(__file__).resolve().parents[6] / "doc" / "logo" / "zarr-pink-horizontal.svg" + +# Cells of the repository logo's glyph (x right, y up), as in doc/logo/logo.py's "logo-zarr" +# variant: the wordmark replaces the top edge's three rightmost cubes. +GLYPH = [(0, y) for y in range(7)] + [(x, 6) for x in range(1, 4)] \ + + [(2, 0), (3, 0), (4, 0), (4, 1)] \ + + [(x, y) for x in (2, 4, 6) for y in (2, 3, 4)] + + +def create_logo_spots_and_labels(): + """ + Creates two related 2D OME-Zarr v0.5 datasets with identical extents: + - logo_spots.ome.zarr → uint8 intensity image: Gaussian spots laid out like the + repository logo's cubes, the blurred Zarr wordmark, and noise + - logo_labels.ome.zarr → uint16 segmentation, one label id per spot and per letter + Both have 2 multiscale levels (Y, X), so they overlay exactly in a viewer, + and OMERO metadata with a channel color and contrast limits. + """ + + Y, X = 512, 512 + pitch = 60 # px per glyph cell + left, top = 40, 50 # px from the image corner to the top left cell's corner + rng = np.random.default_rng(42) + + yy, xx = np.mgrid[0:Y, 0:X] + intensity = np.zeros((Y, X), dtype=np.float64) + labels = np.zeros((Y, X), dtype=np.uint16) + best = np.zeros((Y, X), dtype=np.float64) + + # ---------------------------------------------------------------------- + # One spot per cube, jittered, stretched and dimmed so the grid is not exact + # ---------------------------------------------------------------------- + for i, (cx, cy) in enumerate(GLYPH, start=1): + x = left + (cx + 0.5) * pitch + rng.normal(0, 6) + y = top + (6 - cy + 0.5) * pitch + rng.normal(0, 6) + sx, sy = rng.uniform(10, 16, size=2) + spot = np.exp(-(xx - x) ** 2 / (2 * sx ** 2) - (yy - y) ** 2 / (2 * sy ** 2)) * rng.uniform(0.7, 1.0) + intensity += spot + # pixel belongs to the spot that is brightest there, if above half its max + mask = (spot > 0.5 * spot.max()) & (spot > best) + labels[mask] = i + best = np.maximum(best, spot) + intensity /= intensity.max() + + # --------------------------------------------------------------------- + # The wordmark right of the top edge, blurred like the spots: each letter + # scaled to its own peak, dimmed on its own, and labelled where above half + # its peak, like a spot. The outlined "z" is filled first, as a blurred + # outline would come out a dim ring. + # --------------------------------------------------------------------- + word = wordmark(width=int(3.2 * pitch)) + cuts = letter_cuts(word > 0.5) + word[:, :cuts[0]] = np.maximum(word[:, :cuts[0]], holes(word[:, :cuts[0]] > 0.5)) + pad = 12 # px around the wordmark for the blur to spread into + word = np.pad(word, pad) + letter = np.searchsorted(cuts, np.arange(word.shape[1]) - pad) # letter index per column + glow = blur(word, sigma=3) + peak = np.array([glow[:, letter == j].max() for j in range(len(cuts) + 1)]) + glow /= peak[letter] + mask = glow > 0.5 + glow *= 0.9 * rng.uniform(0.7, 1.0, size=len(cuts) + 1)[letter] + wx, wy = left + int(4.1 * pitch) - pad, top + (pitch - word.shape[0]) // 2 + intensity[wy:wy + word.shape[0], wx:wx + word.shape[1]] += glow + region = labels[wy:wy + word.shape[0], wx:wx + word.shape[1]] + region[mask] = (len(GLYPH) + 1 + np.broadcast_to(letter, word.shape))[mask] + n_labels = int(labels.max()) + + intensity = intensity * 200 + rng.normal(20, 5, (Y, X)) + image = np.clip(intensity, 0, 255).astype(np.uint8) + + axes = [ + {"name": "y", "type": "space"}, + {"name": "x", "type": "space"}, + ] + + # Raw data in green (GFP-like), from the background level to the brightest spot + write(image, axes, "logo_spots.ome.zarr", "logo_spots", + omero_channel("spots", "00FF00", start=20, end=230, min=0, max=255)) + # Labels in magenta; a window of 0..1 shows every label id >= 1 at full brightness + write(labels, axes, "logo_labels.ome.zarr", "logo_labels", + omero_channel("labels", "FF00FF", start=0, end=1, min=0, max=n_labels)) + + +def wordmark(width): + """The "zarr" of the Zarr logo as coverage in 0..1, `width` px wide, without the + cube mark: rasterized, and cut at the widest run of empty columns.""" + png = cairosvg.svg2png(url=str(ZARR_SVG), output_width=1800) + art = Image.open(io.BytesIO(png)).getchannel("A") + alpha = np.array(art.crop(art.getbbox())) + runs = empty_column_runs(alpha > 0) + _, gap_end = max(runs, key=lambda r: r[1] - r[0]) + word = Image.fromarray(alpha[:, gap_end:]) + word = word.crop(word.getbbox()) + word = word.resize((width, round(word.height * width / word.width)), Image.LANCZOS) + return np.array(word) / 255.0 + + +def empty_column_runs(mask): + """(start, end) of every run of empty columns in `mask`.""" + empty = list(~mask.any(axis=0)) + [False] + runs, start = [], None + for x, blank in enumerate(empty): + if blank and start is None: + start = x + elif not blank and start is not None: + runs.append((start, x)) + start = None + return runs + + +def letter_cuts(mask): + """The columns that split `mask` into letters: the middle of each run of empty columns.""" + return [(start + end) // 2 for start, end in empty_column_runs(mask)] + + +def holes(mask): + """1 where `mask` encloses background, else 0: what a flood fill from outside does not reach.""" + outside = Image.fromarray(np.pad(mask, 1).astype(np.uint8) * 255).copy() # floodfill cannot write to fromarray's buffer + ImageDraw.floodfill(outside, (0, 0), 128) + return (np.array(outside)[1:-1, 1:-1] == 0).astype(np.float64) + + +def blur(a, sigma): + """`a` convolved with a Gaussian of `sigma` px, separably.""" + r = int(3 * sigma) + k = np.exp(-np.arange(-r, r + 1) ** 2 / (2 * sigma ** 2)) + k /= k.sum() + a = np.apply_along_axis(np.convolve, 0, a, k, mode="same") + return np.apply_along_axis(np.convolve, 1, a, k, mode="same") + + +def omero_channel(label, color, start, end, min, max): + return { + "channels": [{ + "label": label, + "color": color, + "active": True, + "window": {"start": start, "end": end, "min": min, "max": max}, + }], + "rdefs": {"model": "color"}, + } + + +def write(data, axes, path, name, omero): + store = parse_url(path, mode="w").store + root = zarr.group(store=store, overwrite=True, zarr_format=3) + write_multiscale( + [data, data[::2, ::2]], + group=root, + axes=axes, + fmt=FormatV05(), + storage_options=dict(chunks=(256, 256)), + name=name, + metadata={"omero": omero} + ) + print(f"OME-Zarr v0.5 written to {path}") + + +if __name__ == "__main__": + create_logo_spots_and_labels() diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/0/0 b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/0/0 new file mode 100644 index 0000000000000000000000000000000000000000..ec49dcf3cfeba8e6f9a0c6188f016ad0301f15ce GIT binary patch literal 780 zcmV+n1M~bSwJ-gk000621sDLx000LF00;mG2?ziH3J3`X00spS5d{zc1cP>{!ml;* z7j^>@fFN)P7{L(;K?o28AwUQagds2lL_h>YK#VblDaVM2>&6%bi~9>Vv)`8qCiujE(?@4 zcoaa#kBP=@c0*nNqfhtNd+u$toRZD*$U=^No{#yTkNF=c&I&H(4=&~pF6Iv|#vkxF z>-m`6Y;xAnW%sx?9~?hNqCef2aJ}~}x~tR0IgN)Fwyxsn_IL=8AEvPt^eh1mWwp>g zDynhp;8jna3Ol$nUUPgMgzC$tu9POn$*HU#f1kmT0D^KmE)t*Sw3^Y)oK*%KXnf;s zc64>X$g`PVgrc7mWl#)>*Y$*&@@a89-&z$jI+&GDi(Bda87y14R6fZ*CYk2+ios78 z{aUU+N2}BZ1Sk5nT>Ii_^aJ1IqSa09Cbi!Oe!ebpZfe7My&AyAOKd06QmXcH|47Ia zk*uHHYgPA0E*Iw}RmRpP{*T;6;oKrKwl?v9BvyBSxi2sEJJBE6ac;RM4OG90{>ZO< z>?M=LqA3rALc^_IO=FNLf|MUMz3i;g0pF8>7IiEYZ%$;)y5h6s05FtW@2oWMgh!7F6ZQHhKnx<)*rfHg{ zX_}^Knx<)*rUio{sG{s0Vh&=N&QIJ?B9z+Yt?f7p!p`#& za#XeM*_)pvV4;GRrc zG?mj>+hsI{ZI8aQ+^BzaJt~w0MqOekwEjhpdKn;;8YsKWev}p-2zPKPsN`p&A*(A0 zNiva=U50kl6m_y{qK@4Ko!&9-MjdccYLGay)}YnZ1zq$maO)b8D@hctr78(3>WKTQ z3Sdq48vMF~a81@46uTnn2-^Y1E-3Y3uk~ijP58hApc|fb1k&z+>KnOnBRs(S(cNez9B?X=9dQ#5@RjD*9;E}O#LG_w zg!QMz=@4hQKRJkm(C}F1)A0iT77)FM9FU#im^({}QC%q15E$jbZTo?5>{ zE;yXVk0*#q@-J}6T!O48$YV@7e%sI-vn?j$l0LV%K0 zHoLn6@mP>9u3yG^BIR|ED@xX4GR+-N2*t}PGi^`e2pQiuAyV&`J>W)|#7L5_Y?09z z7X(UXyz)=mk&#mdVR2>ut>;l(MNzTtYYAJ=|;Jku{i@c;Zob~=98lS z8o)dZ9y{;1@Xid^N#?6x^8uJYf8L;i6TA5%m$T?G%w~ui5Wp+}>R%iNTquw0>QOU< zlN*%*Tnfy_8pdHO`peN*szQ2<8j`k(uP00%Bnde0Rk4#ROJg4}80$Sg?RbhsITXBD zJz2ZMCy~UA)@H-JWEe!g^raK%{qO{CfOe$;v8GH?z~NOaxMhLTM`vSzZj{CZE@l81 z{4MV=CCMf)*QOnSNq__VHy+D5i4Iq)PAmAc9Kyoo5Y7;sg%e+dIl>22Fn@SW*(H2P zW#C}X z)niK7yWl^)i>|u>SpIpQ*J`*DAU+is(@d40$JTbAOQZo;)o8lsM{k3#TS!cZTprRt z!O4#AbH_aF>x#Mmqhzv<4`drr>^C%#Py(9{g*upZNR+`Qg+dk9E)+^(w@|2su?s~l n%qkSCu<%35jj|4fL?~gR^ZwIod>niC+;5o%tv%U;sFpw*Zj31n literal 0 HcmV?d00001 diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/1/0 b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/1/0 new file mode 100644 index 0000000000000000000000000000000000000000..f18ba3b3ee3fa1854cf6c4920af3d52bc09578a4 GIT binary patch literal 744 zcmdPcs{eNZ0|OJoQ!$3LSt|WO$^t;10+S+xJcAO$>uYPHx98p6RSIHcuibj>*TDn< z8J>d)5(fl$966XQmQD%s(u5K3pa8CkDSe6YCCQgPC2m&Ab0?njfO1VPc_{aq9Ftf) zC5Y+yoZ@qy>T^pjd16;9(s+JF;Sc7G?3zoNg&Y(FSOi!&nOGb-1ejVF6dfQ`fC7-~tzg)#iK^DcSGl9N2qEZsM&)#Z0HVZys9Rx@ud2V4uz(Hl8p>#_CYDMZ&5A zUgy(3sJycF5Vr7KDY`l$`rr+{Q*6E)M2arRPV>4cDD=q4Y=#9#3+tZXwELUZ6$>Q) zT~lyFp?3AIcs4_~RRS$uCT@!+ba+Vwhb`>zieO$D(B&0zIHhyLtDyEIQBE(O?vlA{ zf?`Axb@uOCv({+xf;@4F3zIgy3Ro1&zQOCGgsoi6%?5rI(Nhnn$hJDI3}H1|p&EN- zLHh#7K!u$)Z)SLV2`9v67Wlc?XDRlHRnIu*cZyf4pD{Lqy zeCzmt&u99cb&>Zqm~Jca^~#8RUVMeK>ivYdtEau1%KCkk?W9{qFE-4$K0!Lersc-o zM)t-=M*rj4)5K-of2iYJVfDCh=CfPbLRY=nC0T527o^|#v|jE*5vPpd!wwtuhpv`x z?dqSGG@Cz2jMEc1;?{XqiXkw{|{tbcW?H1e}8+8p}5++z2BJ|zVQc?+Z>zg>e#Sv@807lbz5dk zo+eV!Iz1j2R*ZT=KCLUs7)Bp3w zFv(;=)FX2i50}rHwIU~(`hPIkvpo`)7ZOzb88XM?yZ4c6s~8+7*qvMw(em<1NtYG? DHik*` literal 0 HcmV?d00001 diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/1/1 b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/c/1/1 new file mode 100644 index 0000000000000000000000000000000000000000..895299880a372a82857adcad969716044a6bb815 GIT binary patch literal 596 zcmV-a0;~NfwJ-gk00062MG^os0{{RM783v#023Af695(f6951c7620f6951c022Tc z6A~2w6#x|y4h;+p4GjPQ00000f#RrELkA`5y#|{_2aa{1%vMn7(D;x1_!fMK|qCPna zw+&m>NRGO>$Z>MJX@>3A;`eZjUBJb;(b{kOLe?rIIBQ)gmfEvd?d@y+t?9JKNzgK& zzT#)D^t?be-s2KscC*zZNBQ%#`h4j}pC0s{OTbrLtOmiwhZNhAmYk(=v+p5Y4U9@te#!~E#9iNdYaeC*Za?<^4e;ik z&Bbd){1>+DwlPS{8j6YSu)`DUP{FGg1dYlBt^uORr3~dZ-Bj0t_0XhDa1W3~E)m;w i5lPAwVyQypB28sjwv#BbO9CW2Q%`9m*QqE~HCYX@x(!MI literal 0 HcmV?d00001 diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/zarr.json b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/zarr.json new file mode 100644 index 000000000..4806ee13b --- /dev/null +++ b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s0/zarr.json @@ -0,0 +1,46 @@ +{ + "shape": [ + 512, + 512 + ], + "data_type": "uint16", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 256, + 256 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes", + "configuration": { + "endian": "little" + } + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "dimension_names": [ + "y", + "x" + ], + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} \ No newline at end of file diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s1/c/0/0 b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr/s1/c/0/0 new file mode 100644 index 0000000000000000000000000000000000000000..dfe6afacd737f5012f7932fc24e8e21fe7f218c5 GIT binary patch literal 1485 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 z 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 literal 0 HcmV?d00001 diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/c/1/0 b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/c/1/0 new file mode 100644 index 0000000000000000000000000000000000000000..04646ac44affe7c9a6ab132610d5220f9f25d721 GIT binary patch literal 50873 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

b#6g%Wmfh!}iSD`NPV6#1}w6B+3g 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 z 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 literal 0 HcmV?d00001 diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/c/1/1 b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s0/c/1/1 new file mode 100644 index 0000000000000000000000000000000000000000..089cad661a6e381a6744cd71899e38f9fead2af6 GIT binary patch literal 48229 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

~@yq_?{kcMp~lRGuJoo{pn0QVv_bt8LitS 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 literal 0 HcmV?d00001 diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s1/zarr.json b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s1/zarr.json new file mode 100644 index 000000000..9ac35f8bd --- /dev/null +++ b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/s1/zarr.json @@ -0,0 +1,43 @@ +{ + "shape": [ + 256, + 256 + ], + "data_type": "uint8", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 256, + 256 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": 0, + "codecs": [ + { + "name": "bytes" + }, + { + "name": "zstd", + "configuration": { + "level": 0, + "checksum": false + } + } + ], + "attributes": {}, + "dimension_names": [ + "y", + "x" + ], + "zarr_format": 3, + "node_type": "array", + "storage_transformers": [] +} \ No newline at end of file diff --git a/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/zarr.json b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/zarr.json new file mode 100644 index 000000000..d51c6cde7 --- /dev/null +++ b/test-shared/resources/ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr/zarr.json @@ -0,0 +1,68 @@ +{ + "attributes": { + "ome": { + "omero": { + "channels": [ + { + "label": "spots", + "color": "00FF00", + "active": true, + "window": { + "start": 20, + "end": 230, + "min": 0, + "max": 255 + } + } + ], + "rdefs": { + "model": "color" + } + }, + "version": "0.5", + "multiscales": [ + { + "datasets": [ + { + "path": "s0", + "coordinateTransformations": [ + { + "type": "scale", + "scale": [ + 1.0, + 1.0 + ] + } + ] + }, + { + "path": "s1", + "coordinateTransformations": [ + { + "type": "scale", + "scale": [ + 2.0, + 2.0 + ] + } + ] + } + ], + "name": "logo_spots", + "axes": [ + { + "name": "y", + "type": "space" + }, + { + "name": "x", + "type": "space" + } + ] + } + ] + } + }, + "zarr_format": 3, + "node_type": "group" +} \ No newline at end of file From 32b7eb4952314eb16eaed6b6ea6f73a5941788c6 Mon Sep 17 00:00:00 2001 From: hahmann Date: Wed, 7 Oct 2026 15:15:59 +0200 Subject: [PATCH 02/25] Let a BDV window's active pyramidal follow the current source of the BDV PyramidalBdv now knows the BdvHandle showing it and whether one of its sources is BDV's current source. BdvUtils registers it with PyramidalService, such that a BDV window showing several pyramidals reports the selected one. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../main/java/ome/zarr/fiji/PyramidalBdv.java | 28 +++++++++++++++++++ .../zarr/fiji/plugins/PyramidalService.java | 5 ++-- .../java/ome/zarr/fiji/util/BdvUtils.java | 23 +++++++++++++-- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/PyramidalBdv.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/PyramidalBdv.java index 524728744..33426b21b 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/PyramidalBdv.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/PyramidalBdv.java @@ -52,6 +52,7 @@ import bdv.BigDataViewer; import bdv.cache.SharedQueue; +import bdv.util.BdvHandle; import bdv.util.RandomAccessibleIntervalMipmapSource4D; import bdv.util.volatiles.VolatileViews; import bdv.viewer.SourceAndConverter; @@ -69,6 +70,8 @@ public class PyramidalBdv< T extends NativeType< T > & RealType< T > > extends A private final List< SourceAndConverter< T > > sources; + private BdvHandle bdvHandle; + public PyramidalBdv( final Context context, final PyramidContents< T > contents ) { this.contents = contents; @@ -97,6 +100,31 @@ public String getName() return contents.name; } + /** + * @return the BDV showing this pyramidal, or {@code null} if it is not shown yet + */ + public BdvHandle getBdvHandle() + { + return bdvHandle; + } + + /** + * Records the BDV showing this pyramidal. One per pyramidal: showing the same instance in a + * second BDV replaces the first. + */ + public void setBdvHandle( final BdvHandle bdvHandle ) + { + this.bdvHandle = bdvHandle; + } + + /** + * @return whether the current source of the BDV showing this pyramidal is one of its sources + */ + public boolean isCurrentSource() + { + return bdvHandle != null && sources.contains( bdvHandle.getViewerPanel().state().getCurrentSource() ); + } + @SuppressWarnings( "unchecked" ) private static < T extends NativeType< T > & RealType< T >, V extends Volatile< T > & NativeType< V > & RealType< V > > List< SourceAndConverter< T > > initSourceAndConverters( final PyramidContents< T > contents ) diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java index 2b1be5e68..841b37c1a 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java @@ -80,7 +80,7 @@ public class PyramidalService extends AbstractService implements SciJavaService @Parameter private ObjectService objectService; - /** Registered BDV windows and the dataset each one displays. */ + /** Registered BDV windows, each with the dataset owning its current source. */ private final Map< Window, Pyramidal > bdvWindows = new ConcurrentHashMap<>(); /** ImagePlus instances known to wrap a {@link Pyramidal}, populated on IJ window focus. */ @@ -152,7 +152,8 @@ else if ( window instanceof ImageWindow ) } /** - * Registers a BDV {@code window} and the {@code dataset} it displays, and makes it the active pyramidal. + * Registers a BDV {@code window} and the {@code dataset} owning its current source, replacing + * the window's previous one, and makes it the active pyramidal. */ public void registerBdvWindow( final Window window, final Pyramidal dataset ) { diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java index 2166aaa30..ca94608a3 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java @@ -51,6 +51,7 @@ import bdv.viewer.ConverterSetups; import bdv.viewer.SourceAndConverter; import bdv.viewer.ViewerState; +import bdv.viewer.ViewerStateChange; import ome.zarr.fiji.PyramidalBdv; import ome.zarr.fiji.plugins.PyramidalService; import ome.zarr.imglib2.metadata.Omero; @@ -89,7 +90,7 @@ public static BdvHandle showBdvAndRegisterWindow( final PyramidalBdv< ? > pyrami if ( topLevelContainer instanceof Window ) { final Window window = ( Window ) topLevelContainer; - registerBdvWindow( pyramidalBdv, window, pyramidalService ); + registerBdvWindow( pyramidalBdv, window, bdvHandle, pyramidalService ); } return bdvHandle; } @@ -99,18 +100,34 @@ public static BdvHandle showBdvAndRegisterWindow( final PyramidalBdv< ? > pyrami * registers the BDV window with it for focus tracking. Also installs a listener to decrement the * reference count (and unregister the window) when the BDV window closes. *

+ * A BDV window can show several pyramidals. + * The {@code pyramidalService} maps the BDV Window to the {@code pyramidal} owning BDV's current + * source. + * So {@code pyramidal} is registered now only if its source is current (BDV makes the + * first source added to an empty viewer current). The {@code pyramidal} is updated whenever the user makes one of its + * sources current. + *

* Focus switches themselves are observed centrally by {@link PyramidalService} via the AWT * {@link java.awt.KeyboardFocusManager}, so no per-window focus listener is needed here. * @param pyramidalBdv the {@link PyramidalBdv} the window displays * @param window the top-level window to track + * @param bdvHandle the handle of the BDV in {@code window} * @param pyramidalService the service to register with, or {@code null} to only reference-count */ public static void registerBdvWindow( final PyramidalBdv< ? > pyramidalBdv, final Window window, - final PyramidalService pyramidalService ) + final BdvHandle bdvHandle, final PyramidalService pyramidalService ) { + pyramidalBdv.setBdvHandle( bdvHandle ); pyramidalBdv.incrementReferences(); if ( pyramidalService != null ) - pyramidalService.registerBdvWindow( window, pyramidalBdv ); + { + if ( pyramidalBdv.isCurrentSource() ) + pyramidalService.registerBdvWindow( window, pyramidalBdv ); + bdvHandle.getViewerPanel().state().changeListeners().add( change -> { + if ( change == ViewerStateChange.CURRENT_SOURCE_CHANGED && pyramidalBdv.isCurrentSource() ) + pyramidalService.registerBdvWindow( window, pyramidalBdv ); + } ); + } window.addWindowListener( new WindowAdapter() { @Override From 5d4284d9665b088f814a23b0ddc89970bb044d0c Mon Sep 17 00:00:00 2001 From: hahmann Date: Thu, 8 Oct 2026 14:17:44 +0200 Subject: [PATCH 03/25] Merge BdvUtils' show methods into one BdvUtils had four similarly named show/register methods which become one show method with an optional BDVHandle param (null handle = new window, else add to that BDV). Some helpers are no longer public. The omeroChannels test moves to BdvUtilsTest. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../command/tools/OpenInBDVCommand.java | 2 +- .../main/java/ome/zarr/fiji/read/OmeZarr.java | 2 +- .../java/ome/zarr/fiji/util/BdvUtils.java | 56 +++++++++++++------ .../ome/zarr/fiji/PyramidalBdvTestBase.java | 22 +------- .../java/ome/zarr/fiji/util/BdvUtilsTest.java | 55 ++++++++++++++++++ 5 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java index 6d20ad76e..6c93f8a09 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java @@ -71,6 +71,6 @@ public void run() return; } final PyramidalBdv< ? > pyramidalBdv = new PyramidalBdv<>( this.pyramidal.getContext(), this.pyramidal.getPyramidContents() ); - BdvUtils.showBdvAndRegisterWindow( pyramidalBdv, pyramidalService ); + BdvUtils.show( pyramidalBdv, pyramidalService ); } } diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java index 99a4b77a6..9531a0be2 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java @@ -445,7 +445,7 @@ public BdvHandle showInBdv() return null; final PyramidalBdv< ? > pyramidal = new PyramidalBdv<>( context, readContents() ); final PyramidalService pyramidalService = context.getService( PyramidalService.class ); - final BdvHandle result = BdvUtils.showBdvAndRegisterWindow( pyramidal, pyramidalService ); + final BdvHandle result = BdvUtils.show( pyramidal, pyramidalService ); logger.info( "Opened pyramidal in BigDataViewer: {}", inputUri ); return result; } ); diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java index ca94608a3..47652a56e 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java @@ -54,6 +54,7 @@ import bdv.viewer.ViewerStateChange; import ome.zarr.fiji.PyramidalBdv; import ome.zarr.fiji.plugins.PyramidalService; +import ome.zarr.imglib2.PyramidContents; import ome.zarr.imglib2.metadata.Omero; public class BdvUtils @@ -66,33 +67,52 @@ private BdvUtils() } /** - * Displays the given pyramidal in a BigDataViewer (BDV) window and registers - * it with {@code pyramidalService} for focus tracking.
+ * Displays the given pyramidal in a new BigDataViewer (BDV) window, see + * {@link #show(PyramidalBdv, BdvHandle, PyramidalService)}. + * + * @param pyramidalBdv the input {@link PyramidalBdv} to be displayed in BDV + * @param pyramidalService the service to notify of focus changes, or {@code null} to skip tracking + * @return the {@code BdvHandle} of the new window + */ + public static BdvHandle show( final PyramidalBdv< ? > pyramidalBdv, final PyramidalService pyramidalService ) + { + return show( pyramidalBdv, null, pyramidalService ); + } + + /** + * Displays the given pyramidal in a new BigDataViewer (BDV) window, or adds it to the open one of + * {@code bdvHandle}, applies its OMERO channel properties and registers it with + * {@code pyramidalService} for focus tracking.
* Increments the pyramidal's reference count and decrements it when the window closes. - * If {@code pyramidalService} is non-null, the pyramidal is immediately marked as active. - * Later focus changes are picked up centrally by {@link PyramidalService}. + * Only a new window is moved to the timepoint the OMERO metadata names. When adding, the + * viewer's timepoint is left alone, so the images already shown do not jump. * * @param pyramidalBdv the input {@link PyramidalBdv} to be displayed in BDV + * @param bdvHandle the BDV to add it to, or {@code null} to open a new window * @param pyramidalService the service to notify of focus changes, or {@code null} to skip tracking - * @return a {@code BdvHandle} instance representing the BDV window + * @return the {@code BdvHandle} of the window showing the pyramidal */ - public static BdvHandle showBdvAndRegisterWindow( final PyramidalBdv< ? > pyramidalBdv, final PyramidalService pyramidalService ) + public static BdvHandle show( final PyramidalBdv< ? > pyramidalBdv, final BdvHandle bdvHandle, + final PyramidalService pyramidalService ) { - BdvHandle bdvHandle = BdvFunctions.show( pyramidalBdv.asSources(), pyramidalBdv.getPyramidContents().numTimepoints(), - BdvOptions.options().frameTitle( pyramidalBdv.getName() ) ).getBdvHandle(); + final PyramidContents< ? > pyramidContents = pyramidalBdv.getPyramidContents(); + final BdvOptions options = bdvHandle == null + ? BdvOptions.options().frameTitle( pyramidalBdv.getName() ) + : BdvOptions.options().addTo( bdvHandle ); + final BdvHandle shownIn = BdvFunctions.show( pyramidalBdv.asSources(), pyramidContents.numTimepoints(), options ).getBdvHandle(); - final Omero omero = pyramidalBdv.getPyramidContents().omero; - final List< Omero.Channel > omeroChannels = omeroChannels( omero, pyramidalBdv.asSources().size() ); - setTimepoint( omero, bdvHandle.getViewerPanel().state() ); - setChannelProperties( omeroChannels, pyramidalBdv.asSources(), bdvHandle.getConverterSetups(), bdvHandle.getViewerPanel().state() ); + if ( bdvHandle == null ) + setTimepoint( pyramidContents.omero, shownIn.getViewerPanel().state() ); + final List< Omero.Channel > omeroChannels = omeroChannels( pyramidContents.omero, pyramidalBdv.asSources().size() ); + setChannelProperties( omeroChannels, pyramidalBdv.asSources(), shownIn.getConverterSetups(), shownIn.getViewerPanel().state() ); - Container topLevelContainer = bdvHandle.getViewerPanel().getRootPane().getParent(); + Container topLevelContainer = shownIn.getViewerPanel().getRootPane().getParent(); if ( topLevelContainer instanceof Window ) { final Window window = ( Window ) topLevelContainer; - registerBdvWindow( pyramidalBdv, window, bdvHandle, pyramidalService ); + registerWindow( pyramidalBdv, window, shownIn, pyramidalService ); } - return bdvHandle; + return shownIn; } /** @@ -114,7 +134,7 @@ public static BdvHandle showBdvAndRegisterWindow( final PyramidalBdv< ? > pyrami * @param bdvHandle the handle of the BDV in {@code window} * @param pyramidalService the service to register with, or {@code null} to only reference-count */ - public static void registerBdvWindow( final PyramidalBdv< ? > pyramidalBdv, final Window window, + private static void registerWindow( final PyramidalBdv< ? > pyramidalBdv, final Window window, final BdvHandle bdvHandle, final PyramidalService pyramidalService ) { pyramidalBdv.setBdvHandle( bdvHandle ); @@ -149,7 +169,7 @@ public void windowClosed( final WindowEvent e ) * @param converterSetups the viewer's converter setups, to look {@code sources} up in * @param state the viewer state that decides which sources are visible */ - public static void setChannelProperties( final List< Omero.Channel > omeroChannels, + private static void setChannelProperties( final List< Omero.Channel > omeroChannels, final List< ? extends SourceAndConverter< ? > > sources, final ConverterSetups converterSetups, final ViewerState state ) { @@ -198,7 +218,7 @@ private static void setDisplayRangeAndBounds( final ConverterSetup converterSetu * @param numChannels the number of display sources to be configured * @return the channels to apply, empty if they cannot be applied */ - public static List< Omero.Channel > omeroChannels( final Omero omero, final int numChannels ) + static List< Omero.Channel > omeroChannels( final Omero omero, final int numChannels ) { if ( omero == null || omero.channels == null || omero.channels.isEmpty() ) return Collections.emptyList(); diff --git a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/PyramidalBdvTestBase.java b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/PyramidalBdvTestBase.java index fa2bf8cd0..76eb3f8f1 100644 --- a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/PyramidalBdvTestBase.java +++ b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/PyramidalBdvTestBase.java @@ -56,7 +56,6 @@ import ome.zarr.ZarrTestUtils; import ome.zarr.fiji.util.BdvUtils; import ome.zarr.imglib2.PyramidContents; -import ome.zarr.imglib2.metadata.Omero; /** * Shared parameterized tests for the BigDataViewer Fiji wrapper @@ -227,7 +226,7 @@ default void testConverterSetup( final String resource ) throws URISyntaxExcepti { PyramidContents< ? > contents = read( resource, context ); PyramidalBdv< ? > pyramidalBdv = new PyramidalBdv<>( context, contents ); - BdvHandle bdvHandle = BdvUtils.showBdvAndRegisterWindow( pyramidalBdv, null ); + BdvHandle bdvHandle = BdvUtils.show( pyramidalBdv, null ); List< ConverterSetup > converterSetups = bdvHandle.getConverterSetups().getConverterSetups( pyramidalBdv.asSources() ); assertNotNull( converterSetups ); @@ -269,7 +268,7 @@ default void testOmeroDisplaySettings( final String resource ) throws URISyntaxE // NB: neither example turns a channel off, so switch one off to cover both answers. contents.omero.channels.get( 1 ).active = false; PyramidalBdv< ? > pyramidalBdv = new PyramidalBdv<>( context, contents ); - BdvHandle bdvHandle = BdvUtils.showBdvAndRegisterWindow( pyramidalBdv, null ); + BdvHandle bdvHandle = BdvUtils.show( pyramidalBdv, null ); ViewerState state = bdvHandle.getViewerPanel().state(); assertEquals( 1, state.getCurrentTimepoint(), "the timepoint the rendering defaults name" ); List< ? extends SourceAndConverter< ? > > sources = pyramidalBdv.asSources(); @@ -279,21 +278,4 @@ default void testOmeroDisplaySettings( final String resource ) throws URISyntaxE bdvHandle.close(); } } - - /** Omero channels are applied only when there are as many of them as there are sources. */ - @ParameterizedTest - @ValueSource( strings = { DATASET_5D_V4, DATASET_5D_V5 } ) - default void testOmeroChannelCountMismatch( final String resource ) throws URISyntaxException - { - try (Context context = new Context()) - { - final Omero omero = read( resource, context ).omero; - final int numChannels = omero.channels.size(); - assertEquals( numChannels, BdvUtils.omeroChannels( omero, numChannels ).size(), "as many channels as sources" ); - assertTrue( BdvUtils.omeroChannels( omero, numChannels + 1 ).isEmpty(), "fewer channels than sources" ); - assertTrue( BdvUtils.omeroChannels( omero, numChannels - 1 ).isEmpty(), "more channels than sources" ); - // empty, never null, so callers test isEmpty() and never dereference null - assertTrue( BdvUtils.omeroChannels( null, numChannels ).isEmpty(), "no omero metadata at all" ); - } - } } diff --git a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java new file mode 100644 index 000000000..bb114d128 --- /dev/null +++ b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java @@ -0,0 +1,55 @@ +/*- + * #%L + * OME-Zarr extras for Fiji + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fiji.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import ome.zarr.imglib2.metadata.Omero; + +class BdvUtilsTest +{ + /** Omero channels are applied only when there are as many of them as there are sources. */ + @Test + void testOmeroChannelCountMismatch() + { + final Omero omero = new Omero(); + omero.channels = Arrays.asList( new Omero.Channel(), new Omero.Channel(), new Omero.Channel() ); + final int numChannels = omero.channels.size(); + assertEquals( numChannels, BdvUtils.omeroChannels( omero, numChannels ).size(), "as many channels as sources" ); + assertTrue( BdvUtils.omeroChannels( omero, numChannels + 1 ).isEmpty(), "fewer channels than sources" ); + assertTrue( BdvUtils.omeroChannels( omero, numChannels - 1 ).isEmpty(), "more channels than sources" ); + // empty, never null, so callers test isEmpty() and never dereference null + assertTrue( BdvUtils.omeroChannels( null, numChannels ).isEmpty(), "no omero metadata at all" ); + } +} From 1ec243174dc0cb3fee21894f1ab91ed213ca3b2e Mon Sep 17 00:00:00 2001 From: hahmann Date: Thu, 8 Oct 2026 14:17:44 +0200 Subject: [PATCH 04/25] Add OmeZarr.showInBdv(BdvHandle) Adds the image to an already open BDV instead of opening a new one. null opens a new window, and showInBdv() now delegates to it. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../main/java/ome/zarr/fiji/read/OmeZarr.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java index 9531a0be2..101538ee6 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java @@ -438,6 +438,20 @@ private static boolean confirmWithDialog( final String message ) */ // NB: the return value is for API and script users public BdvHandle showInBdv() + { + return showInBdv( null ); + } + + /** + * Adds the {@link ome.zarr.fiji.Pyramidal} as a {@link PyramidalBdv} to the already open + * BigDataViewer window of {@code bdvHandle}, and registers it with the {@link PyramidalService} lifecycle. + * Unlike {@link #showInBdv()}, it does not change the viewer's timepoint. + * + * @param bdvHandle the BigDataViewer to add to, or {@code null} to open a new window like {@link #showInBdv()} + * @return the resulting {@link BdvHandle}, or {@code null} if opening failed or + * the user declined to open the image. + */ + public BdvHandle showInBdv( final BdvHandle bdvHandle ) { return openPyramidImage( () -> { @@ -445,8 +459,8 @@ public BdvHandle showInBdv() return null; final PyramidalBdv< ? > pyramidal = new PyramidalBdv<>( context, readContents() ); final PyramidalService pyramidalService = context.getService( PyramidalService.class ); - final BdvHandle result = BdvUtils.show( pyramidal, pyramidalService ); - logger.info( "Opened pyramidal in BigDataViewer: {}", inputUri ); + final BdvHandle result = BdvUtils.show( pyramidal, bdvHandle, pyramidalService ); + logger.info( "{} pyramidal in BigDataViewer: {}", bdvHandle == null ? "Opened" : "Added", inputUri ); return result; } ); } From 1e08f1ccc22e0c48aaae658ba4258b9cbc41b628 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 9 Oct 2026 14:03:23 +0200 Subject: [PATCH 05/25] Relabel the BigDataViewer opener as "BigDataViewer (new window)" The label "BigDataViewer" becomes "BigDataViewer (new window)", so it stays distinguishable from an opener that adds to an open BDV. Its name bdv-multi-resolution stays, as it is persisted. Co-Authored-By: Claude Opus 5.5 (1M context) --- README.md | 3 ++- .../fijiui/open/openers/BdvMultiResolutionOpener.java | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 57fcfed01..fdd568889 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,8 @@ What Fiji does with a dropped / pasted / linked OME-Zarr. The options shipped he * **ImageJ (matching resolution)** (initial default): opens the highest single-resolution level that is not wider than the [maximum width](#maximum-width). This avoids loading excessively large images. * **ImageJ (highest resolution)**: opens the highest-resolution level in ImageJ. -* **BigDataViewer**: opens all resolution levels as a multi-resolution source. This is useful for large OME-Zarrs. +* **BigDataViewer (new window)**: opens all resolution levels as a multi-resolution source in a new BigDataViewer + window. This is useful for large OME-Zarrs. Channel names, colors, contrast limits, and the time point are [taken from the OME-Zarr metadata](#read-channel-information-from-ome-zarr-metadata), if available. * **N5 importer dialog**: opens the N5 import dialog at the dropped OME-Zarr. It lists the resolution levels, lets you diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvMultiResolutionOpener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvMultiResolutionOpener.java index 05f5a77fc..2a9d81801 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvMultiResolutionOpener.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvMultiResolutionOpener.java @@ -35,13 +35,12 @@ import org.scijava.plugin.Plugin; /** - * Opens the whole resolution pyramid as a multi-resolution source in - * BigDataViewer, which is the only opener here that does not have to pick a - * single level. + * Opens the whole resolution pyramid as a multi-resolution source in a new + * BigDataViewer window, so it does not have to pick a single level. */ @Plugin( type = OmeZarrOpener.class, name = BdvMultiResolutionOpener.NAME, - label = "BigDataViewer", - description = "Open as a multi-resolution source in BigDataViewer", + label = "BigDataViewer (new window)", + description = "Open as a multi-resolution source in a new BigDataViewer window", iconPath = "/ome/zarr/fijiui/dialog/bdv_icon.png", priority = Priority.NORMAL ) public class BdvMultiResolutionOpener implements OmeZarrOpener { From a811e92567c7bbccdb722b577bf2734bf5c4e784 Mon Sep 17 00:00:00 2001 From: hahmann Date: Thu, 8 Oct 2026 14:15:14 +0200 Subject: [PATCH 06/25] Add "BigDataViewer (add to active window)" opener Adds the image to the BDV holding the active pyramidal via OmeZarr.showInBdv(BdvHandle), or opens a new one if there is none. Co-Authored-By: Claude Opus 5.5 (1M context) --- CLAUDE.md | 6 +- README.md | 3 + doc/DEVELOPERS.md | 4 +- .../open/openers/BdvAddToActiveOpener.java | 64 +++++++++++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java diff --git a/CLAUDE.md b/CLAUDE.md index 28889e415..7afc1f7ac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -240,8 +240,8 @@ i.e. a SciJava plugin anyway. what a persisted setting means (`effectiveOpenerName`). - Shipped openers – `imagej-preferred-resolution`, `imagej-highest-resolution`, `bdv-multi-resolution`, - `n5-importer-dialog`, `n5-viewer-dialog`, `script-editor` – are one class each, next to the `OmeZarrOpenActions` they - drive. Only the extension point itself sits in `ome-zarr-fiji`: that is the artifact a downstream opener compiles + `bdv-add-to-active`, `n5-importer-dialog`, `n5-viewer-dialog`, `script-editor` – are one class each, next to the + `OmeZarrOpenActions` they drive. Only the extension point itself sits in `ome-zarr-fiji`: that is the artifact a downstream opener compiles against, and it stays free of concrete openers. Help is a plain button, not an opener. The selection dialog (`OmeZarrOpenActionChooser`, one icon button per offered opener) is **not** an opener: it opens @@ -329,7 +329,7 @@ registered) would be the alternative. Java package names stay `ome.zarr.*` throu pyramids). - **`ome-zarr-fiji-ui`** – `ome.zarr.fijiui` (+`.open`, `.open.openers`, `.open.options`, `.plugin`, `.plugin.command.*`, `.dialog`, `.util`); the OME-Zarr `IOPlugin` (drag-and-drop and `fiji://` links) in `.plugin`, - Swing dialogs in `.dialog`, the six built-in `OmeZarrOpener`s in `.open.openers`. The SciJava commands sit in three + Swing dialogs in `.dialog`, the seven built-in `OmeZarrOpener`s in `.open.openers`. The SciJava commands sit in three sibling packages under `.plugin.command`, one per menu location: `.fileimport` for the two `File > Import` entries plus their shared `FileImportHelper` (package-private, so its tests live there too), `.tools` for the `Plugins > OME-Zarr` entries, and `.settings` for `OpeningBehaviorSettings`, `S3Settings` and `UserScriptSettings`. diff --git a/README.md b/README.md index fdd568889..49875663f 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,9 @@ What Fiji does with a dropped / pasted / linked OME-Zarr. The options shipped he window. This is useful for large OME-Zarrs. Channel names, colors, contrast limits, and the time point are [taken from the OME-Zarr metadata](#read-channel-information-from-ome-zarr-metadata), if available. +* **BigDataViewer (add to active window)**: adds the image as a multi-resolution source to the BigDataViewer + window that had focus most recently, e.g., to overlay a segmentation on the raw data. Opens a new BigDataViewer + window if none is open, or if an ImageJ image window had focus after it. * **N5 importer dialog**: opens the N5 import dialog at the dropped OME-Zarr. It lists the resolution levels, lets you choose one, possibly crop it, and opens it in ImageJ. * **N5 viewer dialog**: opens the N5 viewer dialog at the dropped OME-Zarr. It lists the resolution levels, lets you diff --git a/doc/DEVELOPERS.md b/doc/DEVELOPERS.md index 8954a856b..9c2943e2f 100644 --- a/doc/DEVELOPERS.md +++ b/doc/DEVELOPERS.md @@ -45,7 +45,7 @@ graph TD | `ome-zarr-n5` | `ome.zarr.n5` | N5-universe backed reader, OME-Zarr v0.3 – v0.5. | `ome-zarr-imglib2`, the N5 stack | | `ome-zarr-zarrjava` | `ome.zarr.zarrjava` | zarr-java backed reader (the default), Zarr v2/v3 i.e. OME-Zarr v0.4 + v0.5. The only backend that reads `.ozx` archives. | `ome-zarr-imglib2`, `dev.zarr:zarr-java` | | `ome-zarr-fiji` | `ome.zarr.fiji` `.read` `.open` `.plugins` `.util` | Opens a pyramid in ImageJ or BigDataViewer, tracks the active window, and defines the `OmeZarrOpener` extension point. No dialogs, no backend dependency — pick one at runtime. | `ome-zarr-imglib2`, bigdataviewer-core, spim_data, imagej-common, ij, scijava-common | -| `ome-zarr-fiji-ui` | `ome.zarr.fijiui` `.open` `.plugin` `.dialog` `.util` | The shipped plugin: `IOPlugin`, clipboard, Swing dialogs, SciJava commands, persisted settings, the six built-in openers. | the four above, scijava-common, script-editor, n5-viewer_fiji | +| `ome-zarr-fiji-ui` | `ome.zarr.fijiui` `.open` `.plugin` `.dialog` `.util` | The shipped plugin: `IOPlugin`, clipboard, Swing dialogs, SciJava commands, persisted settings, the seven built-in openers. | the four above, scijava-common, script-editor, n5-viewer_fiji | Pick the smallest one you need: @@ -248,7 +248,7 @@ jar for free, and the icon is resolved out of the *contributing* jar. | Annotation element | What it does | |------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `name` | Identifies the opener in the persisted settings, so **keep it stable across releases**. The shipped ones are `imagej-preferred-resolution`, `imagej-highest-resolution`, `bdv-multi-resolution`, `n5-importer-dialog`, `n5-viewer-dialog` and `script-editor`. | +| `name` | Identifies the opener in the persisted settings, so **keep it stable across releases**. The shipped ones are `imagej-preferred-resolution`, `imagej-highest-resolution`, `bdv-multi-resolution`, `bdv-add-to-active`, `n5-importer-dialog`, `n5-viewer-dialog` and `script-editor`. | | `label`, `description` | What the user sees in the settings and as the button tooltip. | | `iconPath` | Classpath path to the button icon in your jar. | | `priority` | Orders the openers, and decides which one a user who never configured a choice gets. The shipped openers sit at `Priority.HIGH` and below, so declare `Priority.VERY_HIGH` to be the out-of-the-box default. | diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java new file mode 100644 index 000000000..e44d9dbd9 --- /dev/null +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java @@ -0,0 +1,64 @@ +/*- + * #%L + * OME-Zarr integration into FIJI + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fijiui.open.openers; + +import ome.zarr.fiji.Pyramidal; +import ome.zarr.fiji.PyramidalBdv; +import ome.zarr.fiji.plugins.PyramidalService; +import ome.zarr.fiji.read.OmeZarr; +import ome.zarr.fiji.open.OmeZarrOpener; + +import org.scijava.Priority; +import org.scijava.plugin.Plugin; + +import bdv.util.BdvHandle; + +/** + * Adds the image to the BigDataViewer window holding the active pyramidal, e.g. + * to overlay a segmentation on its raw data. Focusing Fiji's main window, toolbar + * or a dialog keeps that window active; focusing an ImageJ image window does not, + * and then, as when no such window is open, a new BigDataViewer window opens. + */ +@Plugin( type = OmeZarrOpener.class, name = BdvAddToActiveOpener.NAME, + label = "BigDataViewer (add to active window)", + description = "Add as a multi-resolution source to the active BigDataViewer window, or open a new one if there is none", + iconPath = "/ome/zarr/fijiui/dialog/bdv_add_icon.png", priority = Priority.NORMAL - 1 ) +public class BdvAddToActiveOpener implements OmeZarrOpener +{ + /** The stable identifier this opener is persisted under. */ + public static final String NAME = "bdv-add-to-active"; + + @Override + public void open( final OmeZarr omeZarr ) + { + final Pyramidal active = omeZarr.context().getService( PyramidalService.class ).getActivePyramidal(); + final BdvHandle bdvHandle = active instanceof PyramidalBdv ? ( ( PyramidalBdv< ? > ) active ).getBdvHandle() : null; + omeZarr.showInBdv( bdvHandle ); + } +} From 495d1fde6fd8a517445a96f8bd25d153a5d7e675 Mon Sep 17 00:00:00 2001 From: hahmann Date: Thu, 8 Oct 2026 17:54:56 +0200 Subject: [PATCH 07/25] Make stringToUri's error messages independent of the clipboard Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/main/java/ome/zarr/fijiui/util/ClipboardUtils.java | 4 ++-- .../test/java/ome/zarr/fijiui/util/ClipboardUtilsTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/util/ClipboardUtils.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/util/ClipboardUtils.java index c46de2be0..3525765f3 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/util/ClipboardUtils.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/util/ClipboardUtils.java @@ -122,7 +122,7 @@ public static URI stringToUri( final String possibleUri, final Consumer< String { if ( possibleUri == null || possibleUri.trim().isEmpty() ) { - errorHandler.accept( "The clipboard does not contain any text." ); + errorHandler.accept( "There is no text to interpret as a URL or path." ); return null; } final String text = possibleUri.trim(); @@ -155,7 +155,7 @@ public static URI stringToUri( final String possibleUri, final Consumer< String } catch ( InvalidPathException e ) { - errorHandler.accept( "Could not interpret the clipboard contents as a URL or path:\n" + text ); + errorHandler.accept( "Could not interpret the text as a URL or path:\n" + text ); return null; } } diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/util/ClipboardUtilsTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/util/ClipboardUtilsTest.java index ed560a453..08846cf7f 100644 --- a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/util/ClipboardUtilsTest.java +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/util/ClipboardUtilsTest.java @@ -80,7 +80,7 @@ void clipboardReportsError( String clipboardContents ) { assertNull( ClipboardUtils.stringToUri( clipboardContents, errorHandler ) ); assertEquals( 1, errors.size() ); - assertTrue( errors.get( 0 ).contains( "clipboard" ) ); + assertTrue( errors.get( 0 ).contains( "no text" ) ); } @Test @@ -180,7 +180,7 @@ void emptySystemClipboardReportsError() setClipboard( "" ); assertNull( ClipboardUtils.readClipboardAsUri( errorHandler ) ); assertEquals( 1, errors.size() ); - assertTrue( errors.get( 0 ).contains( "clipboard" ) ); + assertTrue( errors.get( 0 ).contains( "no text" ) ); } @Test @@ -211,7 +211,7 @@ public boolean isDataFlavorSupported( DataFlavor f ) }, null ); assertNull( ClipboardUtils.readClipboardAsUri( errorHandler ) ); assertEquals( 1, errors.size() ); - assertTrue( errors.get( 0 ).contains( "clipboard" ) ); + assertTrue( errors.get( 0 ).contains( "no text" ) ); } // --- InvalidPathException branch in stringToUri() --- From b04a5e8fe59f7822b6c97c4e0b0e192ba4a32a77 Mon Sep 17 00:00:00 2001 From: hahmann Date: Thu, 8 Oct 2026 18:17:10 +0200 Subject: [PATCH 08/25] Route BDV opening through OmeZarrOpenActions.showInBdv(BdvHandle) OmeZarrOpenActions gets showInBdv(BdvHandle), mirroring OmeZarr (null opens a new window), and showInBdv() delegates to it. Both BDV openers now go through the class OmeZarrOpenActions (like all other openers). This allows to add UI behaviour for BDV windows in one place (#127). Co-Authored-By: Claude Opus 5.5 (1M context) --- .../zarr/fijiui/open/OmeZarrOpenActions.java | 18 +++++++++++++++--- .../open/openers/BdvAddToActiveOpener.java | 3 ++- .../open/openers/BdvMultiResolutionOpener.java | 3 ++- .../fijiui/open/OmeZarrOpenActionsTest.java | 2 +- .../main/java/ome/zarr/fiji/read/OmeZarr.java | 1 - 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java index 94c58930f..75797d38f 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java @@ -189,7 +189,7 @@ public OmeZarrOpenActions( final OmeZarr omeZarr ) } /** - * Reader for {@code inputUri} using the backend, AWS profile and preferred + * OmeZarr for {@code inputUri} using the backend, AWS profile and preferred * width from {@code settings}, or the defaults when no settings are given. */ private static OmeZarr omeZarrFor( final URI inputUri, final Context context, @@ -262,11 +262,23 @@ public PyramidalDataset showInImageJ( final int resolutionLevel ) * * @return the resulting {@link BdvHandle}, or {@code null} if opening failed */ - // UnusedReturnValue: the dataset is returned for API and script users @SuppressWarnings( "UnusedReturnValue" ) public BdvHandle showInBdv() { - return omeZarr.showInBdv(); + return showInBdv( null ); + } + + /** + * Adds the dataset to the open BigDataViewer of {@code bdvHandle}, or opens it in a new one if + * {@code null}. Delegates to {@link OmeZarr#showInBdv(BdvHandle)}. + * + * @param bdvHandle the BigDataViewer to add to, or {@code null} to open a new window + * @return the resulting {@link BdvHandle}, or {@code null} if opening failed + */ + @SuppressWarnings( "UnusedReturnValue" ) + public BdvHandle showInBdv( final BdvHandle bdvHandle ) + { + return omeZarr.showInBdv( bdvHandle ); } /** diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java index e44d9dbd9..c079175e6 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java @@ -33,6 +33,7 @@ import ome.zarr.fiji.plugins.PyramidalService; import ome.zarr.fiji.read.OmeZarr; import ome.zarr.fiji.open.OmeZarrOpener; +import ome.zarr.fijiui.open.OmeZarrOpenActions; import org.scijava.Priority; import org.scijava.plugin.Plugin; @@ -59,6 +60,6 @@ public void open( final OmeZarr omeZarr ) { final Pyramidal active = omeZarr.context().getService( PyramidalService.class ).getActivePyramidal(); final BdvHandle bdvHandle = active instanceof PyramidalBdv ? ( ( PyramidalBdv< ? > ) active ).getBdvHandle() : null; - omeZarr.showInBdv( bdvHandle ); + new OmeZarrOpenActions( omeZarr ).showInBdv( bdvHandle ); } } diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvMultiResolutionOpener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvMultiResolutionOpener.java index 2a9d81801..b8c97fbdb 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvMultiResolutionOpener.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvMultiResolutionOpener.java @@ -30,6 +30,7 @@ import ome.zarr.fiji.read.OmeZarr; import ome.zarr.fiji.open.OmeZarrOpener; +import ome.zarr.fijiui.open.OmeZarrOpenActions; import org.scijava.Priority; import org.scijava.plugin.Plugin; @@ -50,6 +51,6 @@ public class BdvMultiResolutionOpener implements OmeZarrOpener @Override public void open( final OmeZarr omeZarr ) { - omeZarr.showInBdv(); + new OmeZarrOpenActions( omeZarr ).showInBdv(); } } diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java index 079a4d336..d134c22f7 100644 --- a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java @@ -183,7 +183,7 @@ void openWithSettingsDispatchesToTheConfiguredOpener() throws URISyntaxException // OmeZarr reads nothing, the omeZarr it hands the reader to does. final List< OmeZarr > readers = readerConstruction.constructed(); assertEquals( 4, readers.size() ); - verify( readers.get( 0 ), times( 1 ) ).showInBdv(); + verify( readers.get( 0 ), times( 1 ) ).showInBdv( null ); verify( readers.get( 1 ), times( 1 ) ).showInImageJ( 0 ); verify( readers.get( 2 ), times( 1 ) ).showInImageJ(); verifyNoInteractions( readers.get( 3 ) ); diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java index 101538ee6..207ec76af 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java @@ -436,7 +436,6 @@ private static boolean confirmWithDialog( final String message ) * @return the resulting {@link BdvHandle}, or {@code null} if opening failed or * the user declined to open the image */ - // NB: the return value is for API and script users public BdvHandle showInBdv() { return showInBdv( null ); From e6f32d847930bcc1879e733471c5530f2269d713 Mon Sep 17 00:00:00 2001 From: hahmann Date: Thu, 8 Oct 2026 18:17:10 +0200 Subject: [PATCH 09/25] Add dropping OME-Zarrs onto BigDataViewer windows Dropping files, folders or URLs onto the image area of a BDV windows that were opened through this plugin before adds them to that window, regardless of the configured opener (#127). OmeZarrDropListener parses them like pasted text, checks them with isZarr and reads them with the user's settings. The reading happens in a separate thread. Co-Authored-By: Claude Opus 5.5 (1M context) --- CLAUDE.md | 8 + README.md | 3 + .../zarr/fijiui/open/OmeZarrDropListener.java | 166 ++++++++++++++++++ .../zarr/fijiui/open/OmeZarrOpenActions.java | 10 +- .../command/tools/OpenInBDVCommand.java | 5 +- 5 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java diff --git a/CLAUDE.md b/CLAUDE.md index 7afc1f7ac..cc96b4cac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -104,6 +104,14 @@ image: the `@Parameter( type = ItemIO.OUTPUT )` on the `dataset` field only hands it to the caller, and a script that captures it decides what to do with it. Being plain text rather than a chooser, it accepts `http(s):` too, but *not* `s3:`. +A second one is dropping onto the image area of a BDV window this plugin opened: `OmeZarrDropListener.install` +(called by `OmeZarrOpenActions.showInBdv` for a new window, and by `OpenInBDVCommand`) puts an AWT `DropTarget` on the +viewer's image area, whose `OmeZarrDropListener` adds the dropped OME-Zarr to *that* window via +`OmeZarr.showInBdv( BdvHandle )`, ignoring the configured opener — where it was dropped already says what the user wants. It validates with `isZarr` like every other route (so no `s3:`) and +reads with the persisted backend and preferred width, loaded at drop time. It lives in `fiji-ui` because those settings +do, so a script calling `OmeZarr.showInBdv()` directly gets a window without it. BDV windows opened by other tools get +no drop target: see issue #127 and bigdataviewer/bigdataviewer-core#227. + `OmeZarr.showInImageJ()`/`showInBdv()` return what they showed (`null` on failure or when the user declined) and `OmeZarrOpenActions` mirrors that; the plugin's own call sites want only the side effect, hence the `@SuppressWarnings( "UnusedReturnValue" )` — the values exist for API and script users. diff --git a/README.md b/README.md index 49875663f..cdb5c243f 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,9 @@ If the dropped / pasted / linked target is not recognized as a **OME-Zarr v0.3 - Drop a local OME-Zarr folder or an OME-Zarr URI onto Fiji. What happens next is set in the [Opening Behavior Settings](#opening-behavior-settings). +Dropping onto the image area of a BigDataViewer window opened by this plugin instead adds the OME-Zarr to that +window, e.g., to overlay a segmentation on the raw data. This does not depend on the opening behavior settings. + ### Copy & Paste of OME-Zarr URIs (local folder, http, https, s3) * Supports local paths, http(s) URLs, and `s3://` URIs diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java new file mode 100644 index 000000000..7fa45bb31 --- /dev/null +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java @@ -0,0 +1,166 @@ +/*- + * #%L + * OME-Zarr extras for Fiji + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fijiui.open; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.scijava.Context; +import org.scijava.prefs.PrefService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import bdv.util.BdvHandle; +import ij.IJ; +import ome.zarr.fiji.read.OmeZarr; +import ome.zarr.fijiui.open.options.OmeZarrOpeningSettings; +import ome.zarr.fijiui.util.ClipboardUtils; +import ome.zarr.imglib2.util.ZarrUtils; + +/** + * Lets OME-Zarrs be dropped onto the image area of a BigDataViewer window, which adds them to + * that window via {@link OmeZarr#showInBdv(BdvHandle)}. Accepts files and folders, e.g., from + * Finder or Explorer, and URLs, e.g., from a browser. + *

+ * BDV itself accepts no drops on its image area, so nothing competes with this. Each dropped + * location is parsed like typed or pasted text ({@link ClipboardUtils#stringToUri}) and checked + * with {@link ZarrUtils#isZarr(URI)}, like every other entry route. Parsing, check and reading run + * on a background thread, since each may go over the network or show an error dialog. + */ +public class OmeZarrDropListener extends DropTargetAdapter +{ + private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() ); + + private final BdvHandle bdvHandle; + + private final Context context; + + private OmeZarrDropListener( final BdvHandle bdvHandle, final Context context ) + { + this.bdvHandle = bdvHandle; + this.context = context; + } + + /** + * Makes the image area of {@code bdvHandle}'s window accept dropped OME-Zarrs. + *

+ * Dropped OME-Zarrs are read with the backend and preferred width the user has configured at + * the time of the drop, and failures are reported via {@code IJ::error}. + * + * @param bdvHandle the BigDataViewer to add dropped OME-Zarrs to + * @param context the SciJava context the settings are read from + */ + public static void install( final BdvHandle bdvHandle, final Context context ) + { + new DropTarget( bdvHandle.getViewerPanel().getDisplayComponent(), DnDConstants.ACTION_COPY, + new OmeZarrDropListener( bdvHandle, context ) ); + } + + @Override + public void dragEnter( final DropTargetDragEvent event ) + { + if ( event.isDataFlavorSupported( DataFlavor.javaFileListFlavor ) + || event.isDataFlavorSupported( DataFlavor.stringFlavor ) ) + event.acceptDrag( DnDConstants.ACTION_COPY ); + else + event.rejectDrag(); + } + + @Override + public void drop( final DropTargetDropEvent event ) + { + event.acceptDrop( DnDConstants.ACTION_COPY ); + final List< String > locations = droppedLocations( event.getTransferable() ); + event.dropComplete( !locations.isEmpty() ); + if ( !locations.isEmpty() ) + new Thread( () -> locations.forEach( this::add ), "OME-Zarr drop onto BigDataViewer" ).start(); + } + + private void add( final String location ) + { + final URI uri = ClipboardUtils.stringToUri( location, IJ::error ); + if ( uri == null ) + return; + if ( !ZarrUtils.isZarr( uri ) ) + { + IJ.error( "The dropped location does not appear to be an OME-Zarr dataset:\n" + uri + "." ); + return; + } + final OmeZarrOpeningSettings settings = + OmeZarrOpeningSettings.loadSettingsFromPreferences( context.getService( PrefService.class ) ); + OmeZarrOpenActions.omeZarrFor( uri, context, settings, IJ::error ).showInBdv( bdvHandle ); + } + + /** + * The locations in a drop, as text: a path per file or folder if the drop carries a file list, + * else one location per line of text, which is how URLs arrive from a browser (and files on some + * Linux desktops, as {@code file:} URIs). They are turned into URIs later, off the event thread, + * since {@link ClipboardUtils#stringToUri} may report an error in a dialog. + */ + static List< String > droppedLocations( final Transferable transferable ) + { + final List< String > locations = new ArrayList<>(); + try + { + if ( transferable.isDataFlavorSupported( DataFlavor.javaFileListFlavor ) ) + { + for ( final Object file : ( List< ? > ) transferable.getTransferData( DataFlavor.javaFileListFlavor ) ) + locations.add( file.toString() ); + } + else if ( transferable.isDataFlavorSupported( DataFlavor.stringFlavor ) ) + { + for ( final String line : ( ( String ) transferable.getTransferData( DataFlavor.stringFlavor ) ).split( "\\R" ) ) + { + final String location = line.trim(); + // text/uri-list allows comment lines starting with '#' + if ( !location.isEmpty() && !location.startsWith( "#" ) ) + locations.add( location ); + } + } + } + catch ( final UnsupportedFlavorException | IOException e ) + { + logger.warn( "Could not read the dropped data: {}", e.getMessage() ); + return Collections.emptyList(); + } + return locations; + } +} diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java index 75797d38f..60c20559e 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java @@ -192,7 +192,7 @@ public OmeZarrOpenActions( final OmeZarr omeZarr ) * OmeZarr for {@code inputUri} using the backend, AWS profile and preferred * width from {@code settings}, or the defaults when no settings are given. */ - private static OmeZarr omeZarrFor( final URI inputUri, final Context context, + static OmeZarr omeZarrFor( final URI inputUri, final Context context, final OmeZarrOpeningSettings settings, final Consumer< String > errorHandler ) { final OmeZarrBackend backend = settings == null ? OmeZarrOpeningSettings.DEFAULT_BACKEND : settings.getBackend(); @@ -270,7 +270,8 @@ public BdvHandle showInBdv() /** * Adds the dataset to the open BigDataViewer of {@code bdvHandle}, or opens it in a new one if - * {@code null}. Delegates to {@link OmeZarr#showInBdv(BdvHandle)}. + * {@code null}. Delegates to {@link OmeZarr#showInBdv(BdvHandle)} and makes a new window + * {@link OmeZarrDropListener#install accept dropped OME-Zarrs}. * * @param bdvHandle the BigDataViewer to add to, or {@code null} to open a new window * @return the resulting {@link BdvHandle}, or {@code null} if opening failed @@ -278,7 +279,10 @@ public BdvHandle showInBdv() @SuppressWarnings( "UnusedReturnValue" ) public BdvHandle showInBdv( final BdvHandle bdvHandle ) { - return omeZarr.showInBdv( bdvHandle ); + final BdvHandle result = omeZarr.showInBdv( bdvHandle ); + if ( bdvHandle == null && result != null ) // NB: only install the drop target on a new window, not on an existing one + OmeZarrDropListener.install( result, omeZarr.context() ); + return result; } /** diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java index 6c93f8a09..6b05f70ea 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java @@ -38,10 +38,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import bdv.util.BdvHandle; import ome.zarr.fiji.Pyramidal; import ome.zarr.fiji.PyramidalBdv; import ome.zarr.fiji.plugins.PyramidalService; import ome.zarr.fiji.util.BdvUtils; +import ome.zarr.fijiui.open.OmeZarrDropListener; @Plugin( type = Command.class, menuPath = "Plugins > OME-Zarr > Open Current OME-Zarr Image in BigDataViewer" ) public class OpenInBDVCommand implements Command @@ -71,6 +73,7 @@ public void run() return; } final PyramidalBdv< ? > pyramidalBdv = new PyramidalBdv<>( this.pyramidal.getContext(), this.pyramidal.getPyramidContents() ); - BdvUtils.show( pyramidalBdv, pyramidalService ); + final BdvHandle bdvHandle = BdvUtils.show( pyramidalBdv, pyramidalService ); + OmeZarrDropListener.install( bdvHandle, this.pyramidal.getContext() ); } } From 6afd97862d592d115a32ebf842f5b57aecae2515 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 9 Oct 2026 15:26:28 +0200 Subject: [PATCH 10/25] Test dropping an OME-Zarr onto a BigDataViewer window OmeZarrDropListener had no test. This one opens logo_spots with the "add to active window" opener, drops logo_labels onto the window's real DropTarget (only the transferable is faked) and checks that BDV then shows both. droppedLocations is unit tested for file lists, text and unreadable drops. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../fijiui/open/OmeZarrDropListenerTest.java | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java new file mode 100644 index 000000000..d5e834431 --- /dev/null +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java @@ -0,0 +1,177 @@ +/*- + * #%L + * OME-Zarr integration into FIJI + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fijiui.open; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.awt.Point; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDropEvent; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nonnull; +import javax.swing.SwingUtilities; + +import org.junit.jupiter.api.Test; +import org.scijava.Context; + +import bdv.util.BdvHandle; +import bdv.viewer.ViewerStateChange; +import ome.zarr.ZarrTestUtils; +import ome.zarr.fiji.PyramidalBdv; +import ome.zarr.fiji.plugins.PyramidalService; +import ome.zarr.fijiui.open.openers.BdvAddToActiveOpener; + +class OmeZarrDropListenerTest +{ + private static final String SPOTS = "ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr"; + + private static final String LABELS = "ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr"; + + /** + * Open the spots with "BigDataViewer (add to active window)", then drop + * the labels folder onto the window's image area, and try to find both in its sources. + */ + @Test + void droppingAFolderAddsItToTheWindow() throws Exception + { + try (Context context = new Context()) + { + new BdvAddToActiveOpener() + .open( OmeZarrOpenActions.withDefaultBackend( ZarrTestUtils.resourcePath( SPOTS ).toUri(), context ) ); + PyramidalBdv< ? > spots = + assertInstanceOf( PyramidalBdv.class, context.getService( PyramidalService.class ).getActivePyramidal() ); + BdvHandle bdvHandle = spots.getBdvHandle(); + assertEquals( 1, bdvHandle.getViewerPanel().state().getSources().size() ); + + DropTarget dropTarget = bdvHandle.getViewerPanel().getDisplayComponent().getDropTarget(); + assertNotNull( dropTarget, "a BDV window opened by this plugin accepts drops" ); + CountDownLatch sourceAdded = new CountDownLatch( 1 ); + bdvHandle.getViewerPanel().state().changeListeners().add( change -> { + if ( change == ViewerStateChange.NUM_SOURCES_CHANGED ) + sourceAdded.countDown(); + } ); + File labels = ZarrTestUtils.resourcePath( LABELS ).toFile(); + SwingUtilities.invokeAndWait( () -> dropTarget.drop( dropEvent( dropTarget, labels ) ) ); + + assertTrue( sourceAdded.await( 30, TimeUnit.SECONDS ), "the drop is read on a background thread" ); + assertEquals( 2, bdvHandle.getViewerPanel().state().getSources().size() ); + + bdvHandle.close(); + SwingUtilities.invokeAndWait( () -> {} ); // wait until all Swing events are processed + } + } + + @Test + void droppedLocationsGivesAPathPerFile() + { + File a = new File( "/data/a.ome.zarr" ), b = new File( "/data/b.ome.zarr" ); + assertEquals( Arrays.asList( a.toString(), b.toString() ), + OmeZarrDropListener.droppedLocations( fileList( Arrays.asList( a, b ) ) ) ); + } + + /** Browsers send URLs as text, one per line, as in {@code text/uri-list}, where '#' starts a comment. */ + @Test + void droppedLocationsGivesALocationPerLineOfText() + { + String uriList = "# dragged from a browser\r\nhttps://example.com/a.ome.zarr\n\n /data/b.ome.zarr \n"; + assertEquals( Arrays.asList( "https://example.com/a.ome.zarr", "/data/b.ome.zarr" ), + OmeZarrDropListener.droppedLocations( new StringSelection( uriList ) ) ); + } + + @Test + void droppedLocationsIsEmptyForNeitherFilesNorText() + { + assertEquals( Collections.emptyList(), OmeZarrDropListener.droppedLocations( mock( Transferable.class ) ) ); + } + + @Test + void droppedLocationsIsEmptyIfTheDataCannotBeRead() throws Exception + { + Transferable unreadable = mock( Transferable.class ); + when( unreadable.isDataFlavorSupported( DataFlavor.stringFlavor ) ).thenReturn( true ); + when( unreadable.getTransferData( DataFlavor.stringFlavor ) ).thenThrow( new IOException( "source app quit" ) ); + assertEquals( Collections.emptyList(), OmeZarrDropListener.droppedLocations( unreadable ) ); + } + + /** {@code files} as Finder or Explorer drag them. */ + private static Transferable fileList( final List< File > files ) + { + return new Transferable() + { + @Override + public DataFlavor[] getTransferDataFlavors() + { + return new DataFlavor[] { DataFlavor.javaFileListFlavor }; + } + + @Override + public boolean isDataFlavorSupported( final DataFlavor flavor ) + { + return DataFlavor.javaFileListFlavor.equals( flavor ); + } + + @Override + public @Nonnull Object getTransferData( final DataFlavor flavor ) + { + return files; + } + }; + } + + /** A drop of {@code file} onto {@code dropTarget}, as Finder or Explorer would make it. */ + private static DropTargetDropEvent dropEvent( final DropTarget dropTarget, final File file ) + { + final Transferable fileList = fileList( Collections.singletonList( file ) ); + return new DropTargetDropEvent( dropTarget.getDropTargetContext(), new Point( 10, 10 ), + DnDConstants.ACTION_COPY, DnDConstants.ACTION_COPY ) + { + @Override + public Transferable getTransferable() + { + return fileList; + } + }; + } +} From d11dce82588d060ac316bcde70c1b49e334e14ee Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 9 Oct 2026 15:40:53 +0200 Subject: [PATCH 11/25] Test the two BigDataViewer openers against each other Opening logo_spots and then logo_labels: "add to active window" opens a new window only for the first and adds the second to it, while "new window" opens one window per image. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../fijiui/open/openers/BdvOpenersTest.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/openers/BdvOpenersTest.java diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/openers/BdvOpenersTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/openers/BdvOpenersTest.java new file mode 100644 index 000000000..fc72bda6b --- /dev/null +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/openers/BdvOpenersTest.java @@ -0,0 +1,104 @@ +/*- + * #%L + * OME-Zarr integration into FIJI + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fijiui.open.openers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; + +import java.util.List; +import java.util.stream.Collectors; + +import javax.swing.SwingUtilities; + +import org.junit.jupiter.api.Test; +import org.scijava.Context; + +import bdv.util.BdvHandle; +import ome.zarr.ZarrTestUtils; +import ome.zarr.fiji.PyramidalBdv; +import ome.zarr.fiji.open.OmeZarrOpener; +import ome.zarr.fiji.plugins.PyramidalService; +import ome.zarr.fijiui.open.OmeZarrOpenActions; + +/** + * "BigDataViewer (add to active window)" against "BigDataViewer (new window)", each opening the + * logo spots and then their labels. + */ +class BdvOpenersTest +{ + private static final String SPOTS = "ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr"; + + private static final String LABELS = "ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr"; + + @Test + void addToActiveOpensANewWindowOnlyIfThereIsNone() throws Exception + { + try (Context context = new Context()) + { + List< BdvHandle > windows = openSpotsThenLabels( new BdvAddToActiveOpener(), context ); + + assertSame( windows.get( 0 ), windows.get( 1 ) ); + assertEquals( 2, windows.get( 0 ).getViewerPanel().state().getSources().size() ); + close( windows ); + } + } + + @Test + void newWindowAlwaysOpensANewWindow() throws Exception + { + try (Context context = new Context()) + { + List< BdvHandle > windows = openSpotsThenLabels( new BdvMultiResolutionOpener(), context ); + + assertNotSame( windows.get( 0 ), windows.get( 1 ) ); + assertEquals( 1, windows.get( 0 ).getViewerPanel().state().getSources().size() ); + assertEquals( 1, windows.get( 1 ).getViewerPanel().state().getSources().size() ); + close( windows ); + } + } + + /** The BDV windows {@code opener} showed the spots and the labels in, one per image. */ + private static List< BdvHandle > openSpotsThenLabels( final OmeZarrOpener opener, final Context context ) throws Exception + { + opener.open( OmeZarrOpenActions.withDefaultBackend( ZarrTestUtils.resourcePath( SPOTS ).toUri(), context ) ); + opener.open( OmeZarrOpenActions.withDefaultBackend( ZarrTestUtils.resourcePath( LABELS ).toUri(), context ) ); + List< BdvHandle > windows = context.getService( PyramidalService.class ).getPyramidals().stream() + .map( pyramidal -> ( ( PyramidalBdv< ? > ) pyramidal ).getBdvHandle() ) + .collect( Collectors.toList() ); + assertEquals( 2, windows.size() ); + return windows; + } + + private static void close( final List< BdvHandle > windows ) throws Exception + { + windows.stream().distinct().forEach( BdvHandle::close ); + SwingUtilities.invokeAndWait( () -> {} ); // wait until all Swing events are processed + } +} From 187a49a6e5a4681f8e976e3b46087dd58b76aaa7 Mon Sep 17 00:00:00 2001 From: hahmann Date: Mon, 12 Oct 2026 16:44:38 +0200 Subject: [PATCH 12/25] Add PyramidalService.getPyramidContents() Lists the OME-Zarrs that are open, once per PyramidContents Co-Authored-By: Claude Opus 5.5 (1M context) --- .../zarr/fiji/plugins/PyramidalService.java | 12 ++++++++ .../fiji/plugins/PyramidalServiceTest.java | 28 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java index 841b37c1a..0a3636f8a 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java @@ -39,6 +39,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import net.imagej.Dataset; import net.imagej.patcher.LegacyInjector; import org.scijava.convert.ConvertService; @@ -50,6 +51,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ome.zarr.fiji.Pyramidal; +import ome.zarr.imglib2.PyramidContents; /** * Tracks which BDV or ImageJ window holding a {@link Pyramidal} was most recently focused. @@ -242,4 +244,14 @@ public List< Pyramidal > getPyramidals() { return objectService.getObjects( Pyramidal.class ); } + + /** + * Returns the {@link PyramidContents} of all registered {@link Pyramidal}s, each once however + * many of them show it. Distinct means the same read, not the same location: an OME-Zarr opened + * twice is read twice and listed twice. + */ + public List< PyramidContents< ? > > getPyramidContents() + { + return getPyramidals().stream().map( Pyramidal::getPyramidContents ).distinct().collect( Collectors.toList() ); + } } diff --git a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/plugins/PyramidalServiceTest.java b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/plugins/PyramidalServiceTest.java index fd1a1e9e2..8874a3236 100644 --- a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/plugins/PyramidalServiceTest.java +++ b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/plugins/PyramidalServiceTest.java @@ -50,6 +50,7 @@ import ome.zarr.fiji.Pyramidal; import ome.zarr.fiji.read.OmeZarr; +import ome.zarr.imglib2.PyramidBackend; import ome.zarr.n5.N5PyramidBackend; import ome.zarr.ZarrTestUtils; @@ -172,4 +173,31 @@ void openTwoBdvDatasets_switchFocusAndCloseInReverseOrder() throws URISyntaxExce assertTrue( pyramidalService.getPyramidals().isEmpty() ); } } + + /** + * An OME-Zarr shown twice, from one read, is listed only once in the pyramid contents. + */ + @Test + void getPyramidContents_listsEachReadOnce() throws URISyntaxException, InterruptedException, InvocationTargetException + { + try (Context context = new Context()) + { + PyramidalService pyramidalService = context.getService( PyramidalService.class ); + PyramidBackend pyramidBackend = new N5PyramidBackend(); + OmeZarr omeZarr2d = new OmeZarr( ZarrTestUtils.resourcePath( ZARR_2D ).toUri(), context, pyramidBackend ); + BdvHandle bdvHandle = omeZarr2d.showInBdv(); + omeZarr2d.showInBdv( bdvHandle ); // the cached read, a second time - 2nd pyramidal (but same pyramid contents) + new OmeZarr( ZarrTestUtils.resourcePath( ZARR_3D ).toUri(), context, pyramidBackend ).showInBdv( bdvHandle ); // a second read, a third pyramidal, but only a second pyramid contents + try + { + assertEquals( 3, pyramidalService.getPyramidals().size() ); + assertEquals( 2, pyramidalService.getPyramidContents().size() ); + } + finally + { + bdvHandle.close(); + SwingUtilities.invokeAndWait( () -> {} ); + } + } + } } From 78bc7d1c7f67d285d287aaa30fc5f064ea43a1b7 Mon Sep 17 00:00:00 2001 From: hahmann Date: Mon, 12 Oct 2026 16:45:33 +0200 Subject: [PATCH 13/25] Add OmeZarrImagesChooser A dialog to choose from a subset from a list of given OME-Zarrs based on PyramidContents Co-Authored-By: Claude Opus 5.5 (1M context) --- .../zarr/fiji/util/OmeZarrImagesChooser.java | 78 ++++++++++++++++ .../fiji/util/OmeZarrImagesChooserTest.java | 93 +++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/OmeZarrImagesChooser.java create mode 100644 ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/OmeZarrImagesChooserTest.java diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/OmeZarrImagesChooser.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/OmeZarrImagesChooser.java new file mode 100644 index 000000000..e575b2813 --- /dev/null +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/OmeZarrImagesChooser.java @@ -0,0 +1,78 @@ +/*- + * #%L + * OME-Zarr integration into FIJI + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fiji.util; + +import java.awt.Dimension; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JOptionPane; + +import ome.zarr.imglib2.PyramidContents; + +/** + * Lets the user tick which of several open OME-Zarrs to show in one viewer window, + * as BigDataViewer's "Open Current Image" does for ImageJ images. + */ +public class OmeZarrImagesChooser +{ + private OmeZarrImagesChooser() + { + // static utility + } + + /** + * Shows a modal dialog with one checkbox per OME-Zarr in {@code candidates}, labeled by its name. + * + * @param candidates the OME-Zarrs to choose from + * @param preselected the one to be ticked when the dialog opens, typically the active pyramid, or {@code null} for none + * @return the ticked OME-Zarrs in the order of {@code candidates}, or an empty list if the user cancels + */ + public static List< PyramidContents< ? > > choose( final List< PyramidContents< ? > > candidates, final PyramidContents< ? > preselected ) + { + final List< JCheckBox > checkboxes = candidates.stream() + .map( contents -> new JCheckBox( contents.name, contents == preselected ) ) + .collect( Collectors.toList() ); + final JOptionPane pane = new JOptionPane( checkboxes.toArray(), JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION ); + final JDialog dialog = pane.createDialog( "OME-Zarr Images to open" ); + dialog.setMinimumSize( new Dimension( 280, 0 ) ); + dialog.setVisible( true ); + dialog.dispose(); + if ( !Integer.valueOf( JOptionPane.OK_OPTION ).equals( pane.getValue() ) ) + return Collections.emptyList(); + return IntStream.range( 0, candidates.size() ) + .filter( i -> checkboxes.get( i ).isSelected() ) + .mapToObj( candidates::get ) + .collect( Collectors.toList() ); + } +} diff --git a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/OmeZarrImagesChooserTest.java b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/OmeZarrImagesChooserTest.java new file mode 100644 index 000000000..21ac4d9e9 --- /dev/null +++ b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/OmeZarrImagesChooserTest.java @@ -0,0 +1,93 @@ +/*- + * #%L + * OME-Zarr extras for Fiji + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fiji.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.awt.event.AWTEventListener; +import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +import org.junit.jupiter.api.Test; + +import ome.zarr.ZarrTestUtils; +import ome.zarr.imglib2.PyramidContents; +import ome.zarr.n5.N5PyramidBackend; + +class OmeZarrImagesChooserTest +{ + private static final String SPOTS = "ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr"; + + private static final String LABELS = "ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr"; + + /** + * Opens the real dialog and, once it is shown, notes which boxes are ticked, then unticks the + * preselected spots, ticks the labels and presses OK. Asserting happens back on the test thread. + */ + @Test + void chooseGivesTheTickedOmeZarrs() throws Exception + { + PyramidContents< ? > spots = N5PyramidBackend.readPyramid( ZarrTestUtils.resourcePath( SPOTS ).toUri() ); + PyramidContents< ? > labels = N5PyramidBackend.readPyramid( ZarrTestUtils.resourcePath( LABELS ).toUri() ); + + List< Boolean > ticksWhenShown = new ArrayList<>(); + AWTEventListener user = event -> { + if ( event.getID() != WindowEvent.WINDOW_OPENED || !( event.getSource() instanceof JDialog ) ) + return; + JOptionPane pane = ( JOptionPane ) ( ( JDialog ) event.getSource() ).getContentPane().getComponent( 0 ); + JCheckBox spotsBox = ( JCheckBox ) ( ( Object[] ) pane.getMessage() )[ 0 ]; + JCheckBox labelsBox = ( JCheckBox ) ( ( Object[] ) pane.getMessage() )[ 1 ]; + ticksWhenShown.addAll( Arrays.asList( spotsBox.isSelected(), labelsBox.isSelected() ) ); + spotsBox.setSelected( false ); + labelsBox.setSelected( true ); + SwingUtilities.invokeLater( () -> pane.setValue( JOptionPane.OK_OPTION ) ); // press OK + }; + Toolkit.getDefaultToolkit().addAWTEventListener( user, AWTEvent.WINDOW_EVENT_MASK ); + try + { + List< PyramidContents< ? > > chosen = OmeZarrImagesChooser.choose( Arrays.asList( spots, labels ), spots ); + assertEquals( Arrays.asList( true, false ), ticksWhenShown, "the spots are preselected" ); + assertEquals( Collections.singletonList( labels ), chosen ); // the labels are chosen + } + finally + { + Toolkit.getDefaultToolkit().removeAWTEventListener( user ); + } + } +} From 90b1baae97f0aefda4f3322d22f2d09f9c3780b5 Mon Sep 17 00:00:00 2001 From: hahmann Date: Tue, 13 Oct 2026 20:57:21 +0200 Subject: [PATCH 14/25] Add BdvUtils.showInOneWindow Shows several OME-Zarrs in one new BDV window, one pyramidal each, so callers no longer thread the BdvHandle through a loop themselves. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../java/ome/zarr/fiji/util/BdvUtils.java | 19 ++++++++ .../java/ome/zarr/fiji/util/BdvUtilsTest.java | 48 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java index 47652a56e..8f1d02562 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java @@ -39,6 +39,7 @@ import net.imglib2.type.numeric.ARGBType; +import org.scijava.Context; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,6 +80,24 @@ public static BdvHandle show( final PyramidalBdv< ? > pyramidalBdv, final Pyrami return show( pyramidalBdv, null, pyramidalService ); } + /** + * Displays each of {@code contents} as a new {@link PyramidalBdv}, all in one new BigDataViewer + * (BDV) window, see {@link #show(PyramidalBdv, BdvHandle, PyramidalService)}. + * + * @param contents the OME-Zarrs to display, in the order their sources are added + * @param context the SciJava context of the new pyramidals + * @param pyramidalService the service to notify of focus changes, or {@code null} to skip tracking + * @return the {@code BdvHandle} of the new window, or {@code null} if {@code contents} is empty + */ + public static BdvHandle showInOneWindow( final List< PyramidContents< ? > > contents, final Context context, + final PyramidalService pyramidalService ) + { + BdvHandle bdvHandle = null; + for ( final PyramidContents< ? > pyramidContents : contents ) + bdvHandle = show( new PyramidalBdv<>( context, pyramidContents ), bdvHandle, pyramidalService ); + return bdvHandle; + } + /** * Displays the given pyramidal in a new BigDataViewer (BDV) window, or adds it to the open one of * {@code bdvHandle}, applies its OMERO channel properties and registers it with diff --git a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java index bb114d128..faeb40dc2 100644 --- a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java +++ b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java @@ -29,16 +29,64 @@ package ome.zarr.fiji.util; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; +import java.util.Collections; + +import javax.swing.SwingUtilities; import org.junit.jupiter.api.Test; +import org.scijava.Context; +import bdv.util.BdvHandle; +import ome.zarr.ZarrTestUtils; +import ome.zarr.fiji.Pyramidal; +import ome.zarr.fiji.PyramidalBdv; +import ome.zarr.fiji.plugins.PyramidalService; +import ome.zarr.imglib2.PyramidContents; import ome.zarr.imglib2.metadata.Omero; +import ome.zarr.n5.N5PyramidBackend; class BdvUtilsTest { + private static final String SPOTS = "ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr"; + + private static final String LABELS = "ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr"; + + /** Two OME-Zarrs end up in one BDV window, as one pyramidal each. */ + @Test + void showInOneWindowShowsAllInOneWindow() throws Exception + { + PyramidContents< ? > spots = N5PyramidBackend.readPyramid( ZarrTestUtils.resourcePath( SPOTS ).toUri() ); + PyramidContents< ? > labels = N5PyramidBackend.readPyramid( ZarrTestUtils.resourcePath( LABELS ).toUri() ); + try (Context context = new Context()) + { + PyramidalService pyramidalService = context.getService( PyramidalService.class ); + BdvHandle bdvHandle = BdvUtils.showInOneWindow( Arrays.asList( spots, labels ), context, pyramidalService ); + try + { + assertEquals( 2, bdvHandle.getViewerPanel().state().getSources().size() ); + assertEquals( 2, pyramidalService.getPyramidals().size() ); + for ( Pyramidal pyramidal : pyramidalService.getPyramidals() ) + assertSame( bdvHandle, ( ( PyramidalBdv< ? > ) pyramidal ).getBdvHandle() ); + } + finally + { + bdvHandle.close(); + SwingUtilities.invokeAndWait( () -> {} ); + } + } + } + + @Test + void showInOneWindowOpensNoWindowForNothing() + { + assertNull( BdvUtils.showInOneWindow( Collections.emptyList(), null, null ) ); + } + /** Omero channels are applied only when there are as many of them as there are sources. */ @Test void testOmeroChannelCountMismatch() From 6e6f5eedb32790336fe3882f3c409aa7f5b36bb4 Mon Sep 17 00:00:00 2001 From: hahmann Date: Mon, 12 Oct 2026 16:49:29 +0200 Subject: [PATCH 15/25] Let "Open Current OME-Zarr Image in BigDataViewer" also open several OME-Zarr images at once. The command previously opened only the active OME-Zarr. With more than one open, in ImageJ or BDV, it now asks which to show, via OmeZarrImagesChooser, and shows them in one new BDV window, like BigDataViewer's own "Open Current Image" does. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../command/tools/OpenInBDVCommand.java | 26 +++++++++++-- .../command/tools/OpenInBDVCommandTest.java | 37 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java index 6b05f70ea..c9ced19c1 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java @@ -29,6 +29,8 @@ package ome.zarr.fijiui.plugin.command.tools; import java.lang.invoke.MethodHandles; +import java.util.Collections; +import java.util.List; import org.scijava.command.Command; import org.scijava.plugin.Parameter; @@ -40,10 +42,11 @@ import bdv.util.BdvHandle; import ome.zarr.fiji.Pyramidal; -import ome.zarr.fiji.PyramidalBdv; import ome.zarr.fiji.plugins.PyramidalService; import ome.zarr.fiji.util.BdvUtils; +import ome.zarr.fiji.util.OmeZarrImagesChooser; import ome.zarr.fijiui.open.OmeZarrDropListener; +import ome.zarr.imglib2.PyramidContents; @Plugin( type = Command.class, menuPath = "Plugins > OME-Zarr > Open Current OME-Zarr Image in BigDataViewer" ) public class OpenInBDVCommand implements Command @@ -72,8 +75,23 @@ public void run() logger.warn( message ); return; } - final PyramidalBdv< ? > pyramidalBdv = new PyramidalBdv<>( this.pyramidal.getContext(), this.pyramidal.getPyramidContents() ); - final BdvHandle bdvHandle = BdvUtils.show( pyramidalBdv, pyramidalService ); - OmeZarrDropListener.install( bdvHandle, this.pyramidal.getContext() ); + final BdvHandle bdvHandle = BdvUtils.showInOneWindow( selectContents(), this.pyramidal.getContext(), pyramidalService ); + if ( bdvHandle != null ) + OmeZarrDropListener.install( bdvHandle, this.pyramidal.getContext() ); + } + + /** + * The OME-Zarrs to show in one BDV window: just the active one, unless more than one + * is open, in ImageJ or BigDataViewer. Each is listed once, however many windows show it. + */ + private List< PyramidContents< ? > > selectContents() + { + final List< PyramidContents< ? > > open = pyramidalService.getPyramidContents(); + if ( open.isEmpty() ) + return Collections.emptyList(); + // for one OME-Zarr, this.pyramidal's contents should be the same as open.get( 0 ) and for headless mode, we don't want to show a chooser dialog, so just return the active one + if ( open.size() == 1 || uiService.isHeadless() ) + return Collections.singletonList( this.pyramidal.getPyramidContents() ); + return OmeZarrImagesChooser.choose( open, this.pyramidal.getPyramidContents() ); // NB: the active one is pre-selected } } diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommandTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommandTest.java index 6711d334f..8ec6930dc 100644 --- a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommandTest.java +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommandTest.java @@ -37,6 +37,7 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.util.List; +import java.util.stream.Collectors; import java.util.concurrent.ExecutionException; import javax.swing.SwingUtilities; @@ -49,9 +50,11 @@ import org.junit.jupiter.api.Test; import org.scijava.Context; import org.scijava.command.CommandService; +import org.scijava.ui.UIService; import ome.zarr.fijiui.open.OmeZarrOpenActions; import ome.zarr.fiji.Pyramidal; +import ome.zarr.fiji.PyramidalBdv; import ome.zarr.fiji.plugins.PyramidalService; import ome.zarr.ZarrTestUtils; @@ -76,6 +79,10 @@ private static void flushEventQueue() throws InterruptedException } } + private static final String SPOTS = "ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr"; + + private static final String LABELS = "ome/zarr/testdata/bdv_testing/logo_labels.ome.zarr"; + // 2 resolution levels: level 0 = 64x64x16, level 1 = 32x32x8 private static final String PYRAMID_RESOURCE = "ome/zarr/testdata/5d_testing/5d_dataset_v5.ome.zarr"; @@ -146,4 +153,34 @@ void runAfterBdvOpenCreatesSecondBdvDataset() throws URISyntaxException, Executi assertSame( firstBdvPyramidal.getPyramidContents(), secondBdvPyramidal.getPyramidContents() ); } } + + /** + * Headless, there is no dialog: with two OME-Zarrs open, only the active one is shown. + */ + @Test + void runHeadlessWithTwoOpenShowsOnlyTheActiveOne() throws Exception + { + try (Context context = new Context()) + { + new OmeZarrOpenActions( ZarrTestUtils.resourcePath( SPOTS ).toUri(), context ).showInImageJ(); + new OmeZarrOpenActions( ZarrTestUtils.resourcePath( LABELS ).toUri(), context ).showInImageJ(); + flushEventQueue(); + Pyramidal active = context.getService( PyramidalService.class ).getActivePyramidal(); + context.getService( UIService.class ).setHeadless( true ); + + context.getService( CommandService.class ).run( OpenInBDVCommand.class, true ).get(); + + List< PyramidalBdv< ? > > shown = bdvPyramidals( context ); + assertEquals( 1, shown.size() ); + assertSame( active.getPyramidContents(), shown.get( 0 ).getPyramidContents() ); + } + } + + private static List< PyramidalBdv< ? > > bdvPyramidals( final Context context ) + { + return context.getService( PyramidalService.class ).getPyramidals().stream() + .filter( PyramidalBdv.class::isInstance ) + .map( pyramidal -> ( PyramidalBdv< ? > ) pyramidal ) + .collect( Collectors.toList() ); + } } From 30ea26d498082c780fd5878384df1984594c8b64 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 13:25:49 +0200 Subject: [PATCH 16/25] Track the active image viewer in ActiveViewerService It is now the only AWT focus listener. PyramidalService follows it instead of listening itself (review of #150). Co-Authored-By: Claude Opus 5.5 (1M context) --- .../fiji/plugins/ActiveViewerService.java | 173 ++++++++++++++++++ .../zarr/fiji/plugins/PyramidalService.java | 53 +++--- .../main/java/ome/zarr/fiji/read/OmeZarr.java | 3 + 3 files changed, 206 insertions(+), 23 deletions(-) create mode 100644 ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/ActiveViewerService.java diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/ActiveViewerService.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/ActiveViewerService.java new file mode 100644 index 000000000..5dcf03111 --- /dev/null +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/ActiveViewerService.java @@ -0,0 +1,173 @@ +/*- + * #%L + * OME-Zarr extras for Fiji + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fiji.plugins; + +import java.awt.Component; +import java.awt.Container; +import java.awt.KeyboardFocusManager; +import java.awt.Window; +import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import javax.swing.SwingUtilities; + +import net.imagej.patcher.LegacyInjector; + +import org.scijava.plugin.Plugin; +import org.scijava.service.AbstractService; +import org.scijava.service.SciJavaService; + +import bdv.viewer.ViewerPanel; +import ij.gui.ImageWindow; + +/** + * Tracks the most recently focused image viewer: an ImageJ {@link ImageWindow} or a BigDataViewer + * {@link ViewerPanel}, no matter which tool opened it. + *

+ * Listens to the {@code "focusOwner"} property of the {@link KeyboardFocusManager} rather than to + * {@code "activeWindow"}, so that the viewers of a window holding two of them (e.g. BigWarp) can be + * told apart. Focus in a BDV window's side panel lies outside its {@link ViewerPanel}, so a window + * holding exactly one viewer counts as that viewer wherever its focus is. All other windows (Fiji's + * main window, dialogs) are ignored, so the last viewer stays active while the user navigates menus. + *

+ * The focus event of a newly shown window arrives only after the call that showed it has returned, + * so whoever opens a viewer reports it via {@link #setActiveViewer}. + */ +@Plugin( type = SciJavaService.class ) +public class ActiveViewerService extends AbstractService implements SciJavaService +{ + static + { + // Patch IJ1 before ij.* classes are loaded during Context startup: + // unpatched IJ1 classes make the later patcher run fail with a LinkageError. + LegacyInjector.preinit(); + } + + /** Weak, so that a closed viewer, and the image data it holds, is not kept alive until the next one becomes active. */ + private final AtomicReference< WeakReference< Component > > activeViewer = new AtomicReference<>( new WeakReference<>( null ) ); + + private final List< Consumer< Component > > listeners = new CopyOnWriteArrayList<>(); + + private PropertyChangeListener focusListener; + + @Override + public void initialize() + { + focusListener = event -> { + final Component viewer = viewerOf( ( Component ) event.getNewValue() ); + if ( viewer != null && viewer != activeViewer.get().get() ) + setActiveViewer( viewer ); + }; + KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener( "focusOwner", focusListener ); + } + + @Override + public void dispose() + { + if ( focusListener != null ) + { + KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener( "focusOwner", focusListener ); + focusListener = null; + } + listeners.clear(); + activeViewer.set( new WeakReference<>( null ) ); + } + + /** + * @return the most recently focused or reported image viewer, an {@link ImageWindow} or a + * {@link ViewerPanel}, or {@code null} if there is none or it has been closed since + */ + public Component getActiveViewer() + { + final Component viewer = activeViewer.get().get(); + return viewer != null && viewer.isShowing() ? viewer : null; + } + + /** + * Makes {@code viewer} the active one, without waiting for its focus event, and tells the + * listeners. + * + * @param viewer an {@link ImageWindow} or a {@link ViewerPanel}, or {@code null} if the viewer + * about to become active is not known yet + */ + public void setActiveViewer( final Component viewer ) + { + activeViewer.set( new WeakReference<>( viewer ) ); + listeners.forEach( listener -> listener.accept( viewer ) ); + } + + /** Calls {@code listener} with each newly active viewer, see {@link #setActiveViewer}. */ + public void addListener( final Consumer< Component > listener ) + { + listeners.add( listener ); + } + + public void removeListener( final Consumer< Component > listener ) + { + listeners.remove( listener ); + } + + /** + * The image viewer {@code focusOwner} belongs to: its {@link ImageWindow}, the + * {@link ViewerPanel} containing it, or else the only one in its window. {@code null} for + * anything else. + */ + static Component viewerOf( final Component focusOwner ) + { + if ( focusOwner == null ) + return null; + final Window window = focusOwner instanceof Window ? ( Window ) focusOwner : SwingUtilities.getWindowAncestor( focusOwner ); + if ( window instanceof ImageWindow ) + return window; + final Component viewerPanel = SwingUtilities.getAncestorOfClass( ViewerPanel.class, focusOwner ); + if ( viewerPanel != null ) + return viewerPanel; + if ( window == null ) + return null; + final List< ViewerPanel > viewerPanels = new ArrayList<>(); + collectViewerPanels( window, viewerPanels ); + return viewerPanels.size() == 1 ? viewerPanels.get( 0 ) : null; + } + + private static void collectViewerPanels( final Container container, final List< ViewerPanel > viewerPanels ) + { + for ( final Component child : container.getComponents() ) + { + if ( child instanceof ViewerPanel ) + viewerPanels.add( ( ViewerPanel ) child ); + else if ( child instanceof Container ) + collectViewerPanels( ( Container ) child, viewerPanels ); + } + } +} diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java index 0a3636f8a..1016f826d 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/plugins/PyramidalService.java @@ -31,15 +31,16 @@ import ij.ImageListener; import ij.ImagePlus; import ij.gui.ImageWindow; -import java.awt.KeyboardFocusManager; +import java.awt.Component; import java.awt.Window; -import java.beans.PropertyChangeListener; import java.lang.invoke.MethodHandles; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.stream.Collectors; +import javax.swing.SwingUtilities; import net.imagej.Dataset; import net.imagej.patcher.LegacyInjector; import org.scijava.convert.ConvertService; @@ -57,10 +58,11 @@ * Tracks which BDV or ImageJ window holding a {@link Pyramidal} was most recently focused. *

* BDV windows are not ImageJ2 displays, and IJ1 {@link ImageWindow} focus changes never reach - * the SciJava {@code EventService}. This service therefore listens at the AWT level via a - * {@code "activeWindow"} {@link PropertyChangeListener} on the {@link KeyboardFocusManager}: - * BDV windows set the active pyramidal directly; ImageJ windows resolve it from the displayed - * image; all other windows (toolbar, dialogs) are ignored so menu navigation doesn't clear state. + * the SciJava {@code EventService}. This service therefore follows the + * {@link ActiveViewerService}, which listens at the AWT level. A BDV window registered here gives + * its pyramidal. An ImageJ window resolves it from the displayed image. BDV windows opened by other + * tools are ignored, as are all non-viewer windows (toolbar, dialogs), so menu navigation doesn't + * clear state. */ @Plugin( type = SciJavaService.class ) public class PyramidalService extends AbstractService implements SciJavaService @@ -82,6 +84,10 @@ public class PyramidalService extends AbstractService implements SciJavaService @Parameter private ObjectService objectService; + @SuppressWarnings( "unused" ) + @Parameter + private ActiveViewerService activeViewerService; + /** Registered BDV windows, each with the dataset owning its current source. */ private final Map< Window, Pyramidal > bdvWindows = new ConcurrentHashMap<>(); @@ -90,7 +96,7 @@ public class PyramidalService extends AbstractService implements SciJavaService private final AtomicReference< Pyramidal > activePyramidal = new AtomicReference<>(); - private PropertyChangeListener focusListener; + private Consumer< Component > activeViewerListener; private ImageListener imageCloseListener; @@ -98,8 +104,8 @@ public class PyramidalService extends AbstractService implements SciJavaService public void initialize() { activePyramidal.set( null ); - focusListener = event -> onActiveWindowChanged( ( Window ) event.getNewValue() ); - KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener( "activeWindow", focusListener ); + activeViewerListener = this::onActiveViewerChanged; + activeViewerService.addListener( activeViewerListener ); imageCloseListener = new ImageListener() { @Override @@ -122,10 +128,10 @@ public void imageClosed( final ImagePlus imagePlus ) @Override public void dispose() { - if ( focusListener != null ) + if ( activeViewerListener != null ) { - KeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener( "activeWindow", focusListener ); - focusListener = null; + activeViewerService.removeListener( activeViewerListener ); + activeViewerListener = null; } if ( imageCloseListener != null ) @@ -138,19 +144,20 @@ public void dispose() } /** - * Updates focus precedence when the active window changes. A registered BDV window gives - * BDV precedence; an ImageJ {@link ImageWindow} returns precedence to the IJ dataset; any other - * window (toolbar, dialogs) is ignored so the last image-window state persists. + * Updates focus precedence when the active viewer changes. An ImageJ {@link ImageWindow} gives + * precedence to the IJ dataset. A BDV viewer in a registered window gives BDV precedence. Any + * other viewer is ignored so the last state persists. */ - private void onActiveWindowChanged( final Window window ) + private void onActiveViewerChanged( final Component viewer ) { - if ( window == null ) - return; - final Pyramidal dataset = bdvWindows.get( window ); - if ( dataset != null ) - notifyBdvWindowFocused( dataset ); - else if ( window instanceof ImageWindow ) - notifyImageJWindowFocused( ( ImageWindow ) window ); + if ( viewer instanceof ImageWindow ) + notifyImageJWindowFocused( ( ImageWindow ) viewer ); + else if ( viewer != null ) + { + final Pyramidal dataset = bdvWindows.get( SwingUtilities.getWindowAncestor( viewer ) ); + if ( dataset != null ) + notifyBdvWindowFocused( dataset ); + } } /** diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java index 207ec76af..c850281ce 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/read/OmeZarr.java @@ -53,6 +53,7 @@ import ome.zarr.imglib2.exceptions.SingleArrayAxesUnknownException; import ome.zarr.fiji.PyramidalBdv; import ome.zarr.fiji.PyramidalDataset; +import ome.zarr.fiji.plugins.ActiveViewerService; import ome.zarr.fiji.plugins.PyramidalService; import ome.zarr.fiji.read.exceptions.NonExistingResolutionLevelException; import ome.zarr.fiji.util.BdvUtils; @@ -386,6 +387,7 @@ private PyramidalDataset showAsDataset( final PyramidContents< ? > contents, fin { final PyramidalDataset dataset = new PyramidalDataset( context, contents, resolutionLevel ); context.getService( UIService.class ).show( dataset ); + context.getService( ActiveViewerService.class ).setActiveViewer( null ); context.getService( PyramidalService.class ).setActivePyramidal( dataset ); logger.info( "Opened dataset at resolution level {} in ImageJ: {}", resolutionLevel, inputUri ); return dataset; @@ -459,6 +461,7 @@ public BdvHandle showInBdv( final BdvHandle bdvHandle ) final PyramidalBdv< ? > pyramidal = new PyramidalBdv<>( context, readContents() ); final PyramidalService pyramidalService = context.getService( PyramidalService.class ); final BdvHandle result = BdvUtils.show( pyramidal, bdvHandle, pyramidalService ); + context.getService( ActiveViewerService.class ).setActiveViewer( result.getViewerPanel() ); logger.info( "{} pyramidal in BigDataViewer: {}", bdvHandle == null ? "Opened" : "Added", inputUri ); return result; } ); From 87377bb99dd4ef0e01b9ed3cc7f0f47ce0bd6ffa Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 13:25:49 +0200 Subject: [PATCH 17/25] Find the active BigDataViewer window directly, not via its pyramidal PyramidalBdv.getBdvHandle() knew only the last of several BDVs, so a window could check the wrong viewer (review of #150). The opener now asks ActiveViewerService for the active viewer and BdvUtils.bdvHandleOf for its handle. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../open/openers/BdvAddToActiveOpener.java | 22 +++++----- .../fijiui/open/OmeZarrDropListenerTest.java | 8 ++-- .../fijiui/open/openers/BdvOpenersTest.java | 20 +++++---- .../main/java/ome/zarr/fiji/PyramidalBdv.java | 28 ------------- .../java/ome/zarr/fiji/util/BdvUtils.java | 41 +++++++++++++++++-- .../java/ome/zarr/fiji/util/BdvUtilsTest.java | 3 +- 6 files changed, 65 insertions(+), 57 deletions(-) diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java index c079175e6..6c6ac7ed2 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/openers/BdvAddToActiveOpener.java @@ -28,23 +28,22 @@ */ package ome.zarr.fijiui.open.openers; -import ome.zarr.fiji.Pyramidal; -import ome.zarr.fiji.PyramidalBdv; -import ome.zarr.fiji.plugins.PyramidalService; +import ome.zarr.fiji.plugins.ActiveViewerService; import ome.zarr.fiji.read.OmeZarr; import ome.zarr.fiji.open.OmeZarrOpener; +import ome.zarr.fiji.util.BdvUtils; import ome.zarr.fijiui.open.OmeZarrOpenActions; import org.scijava.Priority; import org.scijava.plugin.Plugin; -import bdv.util.BdvHandle; - /** - * Adds the image to the BigDataViewer window holding the active pyramidal, e.g. - * to overlay a segmentation on its raw data. Focusing Fiji's main window, toolbar - * or a dialog keeps that window active; focusing an ImageJ image window does not, - * and then, as when no such window is open, a new BigDataViewer window opens. + * Adds the image to the active BigDataViewer window, e.g. to overlay a + * segmentation on its raw data. A BigDataViewer window is active if it is the + * image window focused or opened most recently. Focusing Fiji's main window, + * toolbar or a dialog keeps it active. Focusing an ImageJ image window, or a + * BigDataViewer window another tool opened, does not, and then, as when no + * BigDataViewer window is open, a new one opens. See {@link BdvUtils#bdvHandleOf}. */ @Plugin( type = OmeZarrOpener.class, name = BdvAddToActiveOpener.NAME, label = "BigDataViewer (add to active window)", @@ -58,8 +57,7 @@ public class BdvAddToActiveOpener implements OmeZarrOpener @Override public void open( final OmeZarr omeZarr ) { - final Pyramidal active = omeZarr.context().getService( PyramidalService.class ).getActivePyramidal(); - final BdvHandle bdvHandle = active instanceof PyramidalBdv ? ( ( PyramidalBdv< ? > ) active ).getBdvHandle() : null; - new OmeZarrOpenActions( omeZarr ).showInBdv( bdvHandle ); + final ActiveViewerService activeViewerService = omeZarr.context().getService( ActiveViewerService.class ); + new OmeZarrOpenActions( omeZarr ).showInBdv( BdvUtils.bdvHandleOf( activeViewerService.getActiveViewer() ) ); } } diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java index d5e834431..68b82b84a 100644 --- a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java @@ -60,7 +60,9 @@ import bdv.viewer.ViewerStateChange; import ome.zarr.ZarrTestUtils; import ome.zarr.fiji.PyramidalBdv; +import ome.zarr.fiji.plugins.ActiveViewerService; import ome.zarr.fiji.plugins.PyramidalService; +import ome.zarr.fiji.util.BdvUtils; import ome.zarr.fijiui.open.openers.BdvAddToActiveOpener; class OmeZarrDropListenerTest @@ -80,9 +82,9 @@ void droppingAFolderAddsItToTheWindow() throws Exception { new BdvAddToActiveOpener() .open( OmeZarrOpenActions.withDefaultBackend( ZarrTestUtils.resourcePath( SPOTS ).toUri(), context ) ); - PyramidalBdv< ? > spots = - assertInstanceOf( PyramidalBdv.class, context.getService( PyramidalService.class ).getActivePyramidal() ); - BdvHandle bdvHandle = spots.getBdvHandle(); + assertInstanceOf( PyramidalBdv.class, context.getService( PyramidalService.class ).getActivePyramidal() ); + BdvHandle bdvHandle = BdvUtils.bdvHandleOf( context.getService( ActiveViewerService.class ).getActiveViewer() ); + assertNotNull( bdvHandle ); assertEquals( 1, bdvHandle.getViewerPanel().state().getSources().size() ); DropTarget dropTarget = bdvHandle.getViewerPanel().getDisplayComponent().getDropTarget(); diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/openers/BdvOpenersTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/openers/BdvOpenersTest.java index fc72bda6b..3255566af 100644 --- a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/openers/BdvOpenersTest.java +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/openers/BdvOpenersTest.java @@ -32,8 +32,9 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import javax.swing.SwingUtilities; @@ -42,9 +43,10 @@ import bdv.util.BdvHandle; import ome.zarr.ZarrTestUtils; -import ome.zarr.fiji.PyramidalBdv; import ome.zarr.fiji.open.OmeZarrOpener; +import ome.zarr.fiji.plugins.ActiveViewerService; import ome.zarr.fiji.plugins.PyramidalService; +import ome.zarr.fiji.util.BdvUtils; import ome.zarr.fijiui.open.OmeZarrOpenActions; /** @@ -87,12 +89,14 @@ void newWindowAlwaysOpensANewWindow() throws Exception /** The BDV windows {@code opener} showed the spots and the labels in, one per image. */ private static List< BdvHandle > openSpotsThenLabels( final OmeZarrOpener opener, final Context context ) throws Exception { - opener.open( OmeZarrOpenActions.withDefaultBackend( ZarrTestUtils.resourcePath( SPOTS ).toUri(), context ) ); - opener.open( OmeZarrOpenActions.withDefaultBackend( ZarrTestUtils.resourcePath( LABELS ).toUri(), context ) ); - List< BdvHandle > windows = context.getService( PyramidalService.class ).getPyramidals().stream() - .map( pyramidal -> ( ( PyramidalBdv< ? > ) pyramidal ).getBdvHandle() ) - .collect( Collectors.toList() ); - assertEquals( 2, windows.size() ); + ActiveViewerService activeViewerService = context.getService( ActiveViewerService.class ); + List< BdvHandle > windows = new ArrayList<>(); + for ( String image : Arrays.asList( SPOTS, LABELS ) ) + { + opener.open( OmeZarrOpenActions.withDefaultBackend( ZarrTestUtils.resourcePath( image ).toUri(), context ) ); + windows.add( BdvUtils.bdvHandleOf( activeViewerService.getActiveViewer() ) ); + } + assertEquals( 2, context.getService( PyramidalService.class ).getPyramidals().size() ); return windows; } diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/PyramidalBdv.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/PyramidalBdv.java index 33426b21b..524728744 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/PyramidalBdv.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/PyramidalBdv.java @@ -52,7 +52,6 @@ import bdv.BigDataViewer; import bdv.cache.SharedQueue; -import bdv.util.BdvHandle; import bdv.util.RandomAccessibleIntervalMipmapSource4D; import bdv.util.volatiles.VolatileViews; import bdv.viewer.SourceAndConverter; @@ -70,8 +69,6 @@ public class PyramidalBdv< T extends NativeType< T > & RealType< T > > extends A private final List< SourceAndConverter< T > > sources; - private BdvHandle bdvHandle; - public PyramidalBdv( final Context context, final PyramidContents< T > contents ) { this.contents = contents; @@ -100,31 +97,6 @@ public String getName() return contents.name; } - /** - * @return the BDV showing this pyramidal, or {@code null} if it is not shown yet - */ - public BdvHandle getBdvHandle() - { - return bdvHandle; - } - - /** - * Records the BDV showing this pyramidal. One per pyramidal: showing the same instance in a - * second BDV replaces the first. - */ - public void setBdvHandle( final BdvHandle bdvHandle ) - { - this.bdvHandle = bdvHandle; - } - - /** - * @return whether the current source of the BDV showing this pyramidal is one of its sources - */ - public boolean isCurrentSource() - { - return bdvHandle != null && sources.contains( bdvHandle.getViewerPanel().state().getCurrentSource() ); - } - @SuppressWarnings( "unchecked" ) private static < T extends NativeType< T > & RealType< T >, V extends Volatile< T > & NativeType< V > & RealType< V > > List< SourceAndConverter< T > > initSourceAndConverters( final PyramidContents< T > contents ) diff --git a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java index 8f1d02562..2ce31bd4e 100644 --- a/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java +++ b/ome-zarr-fiji/src/main/java/ome/zarr/fiji/util/BdvUtils.java @@ -29,6 +29,7 @@ package ome.zarr.fiji.util; import java.awt.Color; +import java.awt.Component; import java.awt.Container; import java.awt.Window; import java.awt.event.WindowAdapter; @@ -51,9 +52,11 @@ import bdv.viewer.ConverterSetupBounds; import bdv.viewer.ConverterSetups; import bdv.viewer.SourceAndConverter; +import bdv.viewer.ViewerPanel; import bdv.viewer.ViewerState; import bdv.viewer.ViewerStateChange; import ome.zarr.fiji.PyramidalBdv; +import ome.zarr.fiji.plugins.ActiveViewerService; import ome.zarr.fiji.plugins.PyramidalService; import ome.zarr.imglib2.PyramidContents; import ome.zarr.imglib2.metadata.Omero; @@ -62,6 +65,9 @@ public class BdvUtils { private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() ); + /** The client property of a {@link ViewerPanel} holding its {@link BdvHandle}, see {@link #bdvHandleOf}. */ + private static final String BDV_HANDLE_KEY = BdvHandle.class.getName(); + private BdvUtils() { // prevent instantiation of this class @@ -101,7 +107,7 @@ public static BdvHandle showInOneWindow( final List< PyramidContents< ? > > cont /** * Displays the given pyramidal in a new BigDataViewer (BDV) window, or adds it to the open one of * {@code bdvHandle}, applies its OMERO channel properties and registers it with - * {@code pyramidalService} for focus tracking.
+ * {@code pyramidalService} for focus tracking. {@link #bdvHandleOf} finds its handle.
* Increments the pyramidal's reference count and decrements it when the window closes. * Only a new window is moved to the timepoint the OMERO metadata names. When adding, the * viewer's timepoint is left alone, so the images already shown do not jump. @@ -119,6 +125,7 @@ public static BdvHandle show( final PyramidalBdv< ? > pyramidalBdv, final BdvHan ? BdvOptions.options().frameTitle( pyramidalBdv.getName() ) : BdvOptions.options().addTo( bdvHandle ); final BdvHandle shownIn = BdvFunctions.show( pyramidalBdv.asSources(), pyramidContents.numTimepoints(), options ).getBdvHandle(); + shownIn.getViewerPanel().putClientProperty( BDV_HANDLE_KEY, shownIn ); if ( bdvHandle == null ) setTimepoint( pyramidContents.omero, shownIn.getViewerPanel().state() ); @@ -134,6 +141,24 @@ public static BdvHandle show( final PyramidalBdv< ? > pyramidalBdv, final BdvHan return shownIn; } + /** + * The {@link BdvHandle} of {@code viewer} if it is the viewer of a BigDataViewer shown via + * {@link #show(PyramidalBdv, BdvHandle, PyramidalService)}, else {@code null}, e.g., for an + * ImageJ window or a BigDataViewer opened by another tool. + *

+ * Mirrors the {@code BdvHandle.of( ViewerPanel )} proposed in + * bigdataviewer-core#227, + * including its client property key. Once that exists, this can delegate to it and reach every + * BigDataViewer. + * + * @param viewer an image viewer as returned by {@link ActiveViewerService#getActiveViewer()}, may be {@code null} + * @return the handle to add sources to {@code viewer} with, or {@code null} + */ + public static BdvHandle bdvHandleOf( final Component viewer ) + { + return viewer instanceof ViewerPanel ? ( BdvHandle ) ( ( ViewerPanel ) viewer ).getClientProperty( BDV_HANDLE_KEY ) : null; + } + /** * Increments the reference count for the given {@code pyramidal} and, if {@code pyramidalService} is non-null, * registers the BDV window with it for focus tracking. Also installs a listener to decrement the @@ -156,14 +181,13 @@ public static BdvHandle show( final PyramidalBdv< ? > pyramidalBdv, final BdvHan private static void registerWindow( final PyramidalBdv< ? > pyramidalBdv, final Window window, final BdvHandle bdvHandle, final PyramidalService pyramidalService ) { - pyramidalBdv.setBdvHandle( bdvHandle ); pyramidalBdv.incrementReferences(); if ( pyramidalService != null ) { - if ( pyramidalBdv.isCurrentSource() ) + if ( isCurrentSource( pyramidalBdv, bdvHandle ) ) pyramidalService.registerBdvWindow( window, pyramidalBdv ); bdvHandle.getViewerPanel().state().changeListeners().add( change -> { - if ( change == ViewerStateChange.CURRENT_SOURCE_CHANGED && pyramidalBdv.isCurrentSource() ) + if ( change == ViewerStateChange.CURRENT_SOURCE_CHANGED && isCurrentSource( pyramidalBdv, bdvHandle ) ) pyramidalService.registerBdvWindow( window, pyramidalBdv ); } ); } @@ -179,6 +203,15 @@ public void windowClosed( final WindowEvent e ) } ); } + /** + * Whether the current source of {@code bdvHandle}'s viewer is one of {@code pyramidalBdv}'s + * sources. Checked per viewer, since one pyramidal may be shown in several. + */ + private static boolean isCurrentSource( final PyramidalBdv< ? > pyramidalBdv, final BdvHandle bdvHandle ) + { + return pyramidalBdv.asSources().contains( bdvHandle.getViewerPanel().state().getCurrentSource() ); + } + /** * Colors each of {@code sources} the way the dataset's OMERO metadata asks * for, gives it that channel's display range, and shows or hides it. diff --git a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java index faeb40dc2..62febe0d1 100644 --- a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java +++ b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/util/BdvUtilsTest.java @@ -30,7 +30,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; @@ -71,7 +70,7 @@ void showInOneWindowShowsAllInOneWindow() throws Exception assertEquals( 2, bdvHandle.getViewerPanel().state().getSources().size() ); assertEquals( 2, pyramidalService.getPyramidals().size() ); for ( Pyramidal pyramidal : pyramidalService.getPyramidals() ) - assertSame( bdvHandle, ( ( PyramidalBdv< ? > ) pyramidal ).getBdvHandle() ); + assertTrue( bdvHandle.getViewerPanel().state().getSources().containsAll( ( ( PyramidalBdv< ? > ) pyramidal ).asSources() ) ); } finally { From b6f236cbd0bf2fc0462a51eb3913354678cc0281 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 13:19:42 +0200 Subject: [PATCH 18/25] Document the active-viewer tracking in CLAUDE.md Co-Authored-By: Claude Opus 5.5 (1M context) --- CLAUDE.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index cc96b4cac..f78bd43ed 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -273,9 +273,17 @@ it is not worth the code. A 0.7-and-earlier preference therefore reads as an unk `info` log, nothing thrown – and the next visit to `OpeningBehaviorSettings` overwrites it. `getOpenerName()` returns `null` for "never configured", which is what lets the priority rule above apply. -**Active-window tracking:** `PyramidalService` (a SciJava service) tracks the most-recently-focused `Pyramidal` window -(BDV or ImageJ) via AWT focus listening; `PyramidalPreprocessor` auto-fills any `Pyramidal`-typed command parameter -with the currently active one. +**Active-window tracking:** `ActiveViewerService` is the only AWT focus listener: it tracks the most recently focused +image viewer, an ImageJ `ImageWindow` or a BDV `ViewerPanel`, whoever opened it. `PyramidalService` follows it to track +the active `Pyramidal`, which `PyramidalPreprocessor` auto-fills into any `Pyramidal`-typed command parameter. +"BigDataViewer (add to active window)" asks it for the active viewer and `BdvUtils.bdvHandleOf` for its handle. **Keep +it one listener**: "an ImageJ window takes over, the toolbar and dialogs don't" is a precedence rule both consumers need, +and two trackers drifting apart is what caused the "sticky BDV" bug (`ed658c70`). It listens to `"focusOwner"`, not +`"activeWindow"`, so that two viewers in one window (BigWarp) stay apart. Focus in a BDV side panel lies outside the +`ViewerPanel`, hence the "only viewer in the window" fallback. Focus events arrive after `show()` returns, so +`OmeZarr.showInBdv` and `OmeZarr.showAsDataset` report the new viewer themselves. `bdvHandleOf` reads a client property +`BdvUtils.show` puts on the panel, mirroring the `BdvHandle.of( ViewerPanel )` proposed upstream in +bigdataviewer/bigdataviewer-core#227 — once that exists, delegate to it and BDVs opened by other tools work too. Note: `BdvHandleService` is test/example-only now (`ome-zarr-fiji-ui/src/test/java/ome/zarr/examples/demo/`), not part of the shipped plugin. From 00ffadc79805b6d8caee4b1d298093a975cc5903 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 14:34:25 +0200 Subject: [PATCH 19/25] Make OmeZarrOpenActions.omeZarrFor public as withSettings Named like withDefaultBackend Co-Authored-By: Claude Opus 5.5 (1M context) --- .../zarr/fijiui/open/OmeZarrDropListener.java | 2 +- .../zarr/fijiui/open/OmeZarrOpenActions.java | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java index 7fa45bb31..c3d474ea0 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java @@ -126,7 +126,7 @@ private void add( final String location ) } final OmeZarrOpeningSettings settings = OmeZarrOpeningSettings.loadSettingsFromPreferences( context.getService( PrefService.class ) ); - OmeZarrOpenActions.omeZarrFor( uri, context, settings, IJ::error ).showInBdv( bdvHandle ); + OmeZarrOpenActions.withSettings( uri, context, settings, IJ::error ).showInBdv( bdvHandle ); } /** diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java index 60c20559e..04487772d 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java @@ -90,7 +90,7 @@ public static void openWithSettings( final URI inputUri, final Context context ) { final PrefService prefService = context.getService( PrefService.class ); final OmeZarrOpeningSettings settings = OmeZarrOpeningSettings.loadSettingsFromPreferences( prefService ); - final OmeZarr omeZarr = omeZarrFor( inputUri, context, settings, IJ::error ); + final OmeZarr omeZarr = withSettings( inputUri, context, settings, IJ::error ); final OmeZarrOpenerService openerService = context.getService( OmeZarrOpenerService.class ); if ( openerService == null ) { @@ -172,7 +172,7 @@ public OmeZarrOpenActions( final URI inputUri, final Context context, final OmeZ OmeZarrOpenActions( final URI inputUri, final Context context, final OmeZarrOpeningSettings settings, final Consumer< String > errorHandler ) { - this( omeZarrFor( inputUri, context, settings, errorHandler ) ); + this( withSettings( inputUri, context, settings, errorHandler ) ); } /** @@ -189,10 +189,17 @@ public OmeZarrOpenActions( final OmeZarr omeZarr ) } /** - * OmeZarr for {@code inputUri} using the backend, AWS profile and preferred - * width from {@code settings}, or the defaults when no settings are given. + * Factory for an {@link OmeZarr} that uses the backend, AWS profile and + * preferred width from {@code settings}, or the defaults when no settings are + * given, like {@link #withDefaultBackend}. + * + * @param inputUri the OME-Zarr location to read + * @param context the SciJava context used for display and services + * @param settings the opening settings, or {@code null} for the defaults + * @param errorHandler receives a user-facing message when opening fails + * @return an {@link OmeZarr} for {@code inputUri} using {@code settings} */ - static OmeZarr omeZarrFor( final URI inputUri, final Context context, + public static OmeZarr withSettings( final URI inputUri, final Context context, final OmeZarrOpeningSettings settings, final Consumer< String > errorHandler ) { final OmeZarrBackend backend = settings == null ? OmeZarrOpeningSettings.DEFAULT_BACKEND : settings.getBackend(); From eca31f156c9d07df0503a1a4727d8aa4438287a5 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 14:34:25 +0200 Subject: [PATCH 20/25] Rename OmeZarrOpenerServiceTest.omeZarrFor to omeZarrWithoutBackend Co-Authored-By: Claude Opus 5.5 (1M context) --- .../ome/zarr/fiji/open/OmeZarrOpenerServiceTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/open/OmeZarrOpenerServiceTest.java b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/open/OmeZarrOpenerServiceTest.java index b1d3386f0..89b738863 100644 --- a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/open/OmeZarrOpenerServiceTest.java +++ b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/open/OmeZarrOpenerServiceTest.java @@ -94,7 +94,7 @@ private static PluginInfo< OmeZarrOpener > register( final Context context, return info; } - private static OmeZarr omeZarrFor( final Context context ) + private static OmeZarr omeZarrWithoutBackend( final Context context ) { return new OmeZarr( URI_UNDER_TEST, context, null, null, message -> {} ); } @@ -165,10 +165,10 @@ void openRunsTheNamedOpenerAndReportsAnUnknownName() register( context, RecordingOpener.class, "recording", Priority.NORMAL ); RecordingOpener.opened = 0; - assertTrue( openerService.open( "recording", omeZarrFor( context ) ) ); + assertTrue( openerService.open( "recording", omeZarrWithoutBackend( context ) ) ); assertEquals( 1, RecordingOpener.opened ); - assertFalse( openerService.open( "uninstalled", omeZarrFor( context ) ), + assertFalse( openerService.open( "uninstalled", omeZarrWithoutBackend( context ) ), "An unknown name opens nothing and says so" ); assertEquals( 1, RecordingOpener.opened ); } @@ -182,7 +182,7 @@ void aFailingOpenerDoesNotTakeThePipelineDown() final OmeZarrOpenerService openerService = context.getService( OmeZarrOpenerService.class ); register( context, FailingOpener.class, "failing", Priority.NORMAL ); - assertDoesNotThrow( () -> openerService.open( "failing", omeZarrFor( context ) ) ); + assertDoesNotThrow( () -> openerService.open( "failing", omeZarrWithoutBackend( context ) ) ); } } @@ -192,7 +192,7 @@ void metadataFallsBackFromDescriptionToLabelToName() try (Context context = new Context()) { final OmeZarrOpenerService openerService = context.getService( OmeZarrOpenerService.class ); - final OmeZarr omeZarr = omeZarrFor( context ); + final OmeZarr omeZarr = omeZarrWithoutBackend( context ); // Neither label nor description given, so both fall back to the name. final PluginInfo< OmeZarrOpener > bare = register( context, RecordingOpener.class, "bare", Priority.NORMAL ); From d3546cf82d3f7e9de3ae9478554b93fbd977a1a0 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 14:42:39 +0200 Subject: [PATCH 21/25] Load the opening settings once per drop Not once per dropped location (review of #150). Co-Authored-By: Claude Opus 5.5 (1M context) --- .../java/ome/zarr/fijiui/open/OmeZarrDropListener.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java index c3d474ea0..77b87a7ee 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java @@ -111,10 +111,14 @@ public void drop( final DropTargetDropEvent event ) final List< String > locations = droppedLocations( event.getTransferable() ); event.dropComplete( !locations.isEmpty() ); if ( !locations.isEmpty() ) - new Thread( () -> locations.forEach( this::add ), "OME-Zarr drop onto BigDataViewer" ).start(); + new Thread( () -> { + final OmeZarrOpeningSettings settings = + OmeZarrOpeningSettings.loadSettingsFromPreferences( context.getService( PrefService.class ) ); + locations.forEach( location -> add( location, settings ) ); + }, "OME-Zarr drop onto BigDataViewer" ).start(); } - private void add( final String location ) + private void add( final String location, final OmeZarrOpeningSettings settings ) { final URI uri = ClipboardUtils.stringToUri( location, IJ::error ); if ( uri == null ) @@ -124,8 +128,6 @@ private void add( final String location ) IJ.error( "The dropped location does not appear to be an OME-Zarr dataset:\n" + uri + "." ); return; } - final OmeZarrOpeningSettings settings = - OmeZarrOpeningSettings.loadSettingsFromPreferences( context.getService( PrefService.class ) ); OmeZarrOpenActions.withSettings( uri, context, settings, IJ::error ).showInBdv( bdvHandle ); } From 93af66aec11b2998a734084005784d366d12c590 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 15:12:49 +0200 Subject: [PATCH 22/25] Name the drop's success flag transferSucceeded It reports the data transfer, not the opening (review of #150). Co-Authored-By: Claude Opus 5.5 (1M context) --- .../main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java index 77b87a7ee..5a9889eeb 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java @@ -109,8 +109,9 @@ public void drop( final DropTargetDropEvent event ) { event.acceptDrop( DnDConstants.ACTION_COPY ); final List< String > locations = droppedLocations( event.getTransferable() ); - event.dropComplete( !locations.isEmpty() ); - if ( !locations.isEmpty() ) + final boolean transferSucceeded = !locations.isEmpty(); + event.dropComplete( transferSucceeded ); + if ( transferSucceeded ) new Thread( () -> { final OmeZarrOpeningSettings settings = OmeZarrOpeningSettings.loadSettingsFromPreferences( context.getService( PrefService.class ) ); From 8396469ded0cd17014ea813af52d0339ef682633 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 15:21:54 +0200 Subject: [PATCH 23/25] Rename OmeZarrDropListener to OmeZarrBdvDropListener It only works on BDV windows (review of #150). Co-Authored-By: Claude Opus 5.5 (1M context) --- CLAUDE.md | 4 ++-- ...rrDropListener.java => OmeZarrBdvDropListener.java} | 6 +++--- .../java/ome/zarr/fijiui/open/OmeZarrOpenActions.java | 4 ++-- .../fijiui/plugin/command/tools/OpenInBDVCommand.java | 4 ++-- ...stenerTest.java => OmeZarrBdvDropListenerTest.java} | 10 +++++----- 5 files changed, 14 insertions(+), 14 deletions(-) rename ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/{OmeZarrDropListener.java => OmeZarrBdvDropListener.java} (96%) rename ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/{OmeZarrDropListenerTest.java => OmeZarrBdvDropListenerTest.java} (94%) diff --git a/CLAUDE.md b/CLAUDE.md index f78bd43ed..aaf1bb47c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -104,9 +104,9 @@ image: the `@Parameter( type = ItemIO.OUTPUT )` on the `dataset` field only hands it to the caller, and a script that captures it decides what to do with it. Being plain text rather than a chooser, it accepts `http(s):` too, but *not* `s3:`. -A second one is dropping onto the image area of a BDV window this plugin opened: `OmeZarrDropListener.install` +A second one is dropping onto the image area of a BDV window this plugin opened: `OmeZarrBdvDropListener.install` (called by `OmeZarrOpenActions.showInBdv` for a new window, and by `OpenInBDVCommand`) puts an AWT `DropTarget` on the -viewer's image area, whose `OmeZarrDropListener` adds the dropped OME-Zarr to *that* window via +viewer's image area, whose `OmeZarrBdvDropListener` adds the dropped OME-Zarr to *that* window via `OmeZarr.showInBdv( BdvHandle )`, ignoring the configured opener — where it was dropped already says what the user wants. It validates with `isZarr` like every other route (so no `s3:`) and reads with the persisted backend and preferred width, loaded at drop time. It lives in `fiji-ui` because those settings do, so a script calling `OmeZarr.showInBdv()` directly gets a window without it. BDV windows opened by other tools get diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrBdvDropListener.java similarity index 96% rename from ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java rename to ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrBdvDropListener.java index 5a9889eeb..5d1642507 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrDropListener.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrBdvDropListener.java @@ -65,7 +65,7 @@ * with {@link ZarrUtils#isZarr(URI)}, like every other entry route. Parsing, check and reading run * on a background thread, since each may go over the network or show an error dialog. */ -public class OmeZarrDropListener extends DropTargetAdapter +public class OmeZarrBdvDropListener extends DropTargetAdapter { private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() ); @@ -73,7 +73,7 @@ public class OmeZarrDropListener extends DropTargetAdapter private final Context context; - private OmeZarrDropListener( final BdvHandle bdvHandle, final Context context ) + private OmeZarrBdvDropListener( final BdvHandle bdvHandle, final Context context ) { this.bdvHandle = bdvHandle; this.context = context; @@ -91,7 +91,7 @@ private OmeZarrDropListener( final BdvHandle bdvHandle, final Context context ) public static void install( final BdvHandle bdvHandle, final Context context ) { new DropTarget( bdvHandle.getViewerPanel().getDisplayComponent(), DnDConstants.ACTION_COPY, - new OmeZarrDropListener( bdvHandle, context ) ); + new OmeZarrBdvDropListener( bdvHandle, context ) ); } @Override diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java index 04487772d..2ebf6d97b 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/open/OmeZarrOpenActions.java @@ -278,7 +278,7 @@ public BdvHandle showInBdv() /** * Adds the dataset to the open BigDataViewer of {@code bdvHandle}, or opens it in a new one if * {@code null}. Delegates to {@link OmeZarr#showInBdv(BdvHandle)} and makes a new window - * {@link OmeZarrDropListener#install accept dropped OME-Zarrs}. + * {@link OmeZarrBdvDropListener#install accept dropped OME-Zarrs}. * * @param bdvHandle the BigDataViewer to add to, or {@code null} to open a new window * @return the resulting {@link BdvHandle}, or {@code null} if opening failed @@ -288,7 +288,7 @@ public BdvHandle showInBdv( final BdvHandle bdvHandle ) { final BdvHandle result = omeZarr.showInBdv( bdvHandle ); if ( bdvHandle == null && result != null ) // NB: only install the drop target on a new window, not on an existing one - OmeZarrDropListener.install( result, omeZarr.context() ); + OmeZarrBdvDropListener.install( result, omeZarr.context() ); return result; } diff --git a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java index c9ced19c1..c2cc2e57a 100644 --- a/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java +++ b/ome-zarr-fiji-ui/src/main/java/ome/zarr/fijiui/plugin/command/tools/OpenInBDVCommand.java @@ -45,7 +45,7 @@ import ome.zarr.fiji.plugins.PyramidalService; import ome.zarr.fiji.util.BdvUtils; import ome.zarr.fiji.util.OmeZarrImagesChooser; -import ome.zarr.fijiui.open.OmeZarrDropListener; +import ome.zarr.fijiui.open.OmeZarrBdvDropListener; import ome.zarr.imglib2.PyramidContents; @Plugin( type = Command.class, menuPath = "Plugins > OME-Zarr > Open Current OME-Zarr Image in BigDataViewer" ) @@ -77,7 +77,7 @@ public void run() } final BdvHandle bdvHandle = BdvUtils.showInOneWindow( selectContents(), this.pyramidal.getContext(), pyramidalService ); if ( bdvHandle != null ) - OmeZarrDropListener.install( bdvHandle, this.pyramidal.getContext() ); + OmeZarrBdvDropListener.install( bdvHandle, this.pyramidal.getContext() ); } /** diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrBdvDropListenerTest.java similarity index 94% rename from ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java rename to ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrBdvDropListenerTest.java index 68b82b84a..8c3b249e6 100644 --- a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrDropListenerTest.java +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrBdvDropListenerTest.java @@ -65,7 +65,7 @@ import ome.zarr.fiji.util.BdvUtils; import ome.zarr.fijiui.open.openers.BdvAddToActiveOpener; -class OmeZarrDropListenerTest +class OmeZarrBdvDropListenerTest { private static final String SPOTS = "ome/zarr/testdata/bdv_testing/logo_spots.ome.zarr"; @@ -110,7 +110,7 @@ void droppedLocationsGivesAPathPerFile() { File a = new File( "/data/a.ome.zarr" ), b = new File( "/data/b.ome.zarr" ); assertEquals( Arrays.asList( a.toString(), b.toString() ), - OmeZarrDropListener.droppedLocations( fileList( Arrays.asList( a, b ) ) ) ); + OmeZarrBdvDropListener.droppedLocations( fileList( Arrays.asList( a, b ) ) ) ); } /** Browsers send URLs as text, one per line, as in {@code text/uri-list}, where '#' starts a comment. */ @@ -119,13 +119,13 @@ void droppedLocationsGivesALocationPerLineOfText() { String uriList = "# dragged from a browser\r\nhttps://example.com/a.ome.zarr\n\n /data/b.ome.zarr \n"; assertEquals( Arrays.asList( "https://example.com/a.ome.zarr", "/data/b.ome.zarr" ), - OmeZarrDropListener.droppedLocations( new StringSelection( uriList ) ) ); + OmeZarrBdvDropListener.droppedLocations( new StringSelection( uriList ) ) ); } @Test void droppedLocationsIsEmptyForNeitherFilesNorText() { - assertEquals( Collections.emptyList(), OmeZarrDropListener.droppedLocations( mock( Transferable.class ) ) ); + assertEquals( Collections.emptyList(), OmeZarrBdvDropListener.droppedLocations( mock( Transferable.class ) ) ); } @Test @@ -134,7 +134,7 @@ void droppedLocationsIsEmptyIfTheDataCannotBeRead() throws Exception Transferable unreadable = mock( Transferable.class ); when( unreadable.isDataFlavorSupported( DataFlavor.stringFlavor ) ).thenReturn( true ); when( unreadable.getTransferData( DataFlavor.stringFlavor ) ).thenThrow( new IOException( "source app quit" ) ); - assertEquals( Collections.emptyList(), OmeZarrDropListener.droppedLocations( unreadable ) ); + assertEquals( Collections.emptyList(), OmeZarrBdvDropListener.droppedLocations( unreadable ) ); } /** {@code files} as Finder or Explorer drag them. */ From be85dd3484357193f72ad93a19ec9a05c703a07c Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 15:29:45 +0200 Subject: [PATCH 24/25] Use array initializer shorthand in OmeZarrOpenActionsTest --- .../java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java index d134c22f7..c82e0f4ab 100644 --- a/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java +++ b/ome-zarr-fiji-ui/src/test/java/ome/zarr/fijiui/open/OmeZarrOpenActionsTest.java @@ -916,8 +916,8 @@ void testOpenDifferentResolutionLevels() throws URISyntaxException PyramidalDataset ifLevel1 = assertInstanceOf( PyramidalDataset.class, datasetService.getDatasets().get( 1 ) ); assertFalse( Util.imagesEqual( Cast.unchecked( ijLevel0.getImgPlus().getImg() ), ifLevel1.getImgPlus().getImg() ) ); - long[] expectedDims0 = new long[] { 64, 64, 16, 3, 4 }; - long[] expectedDims1 = new long[] { 32, 32, 8, 3, 4 }; + long[] expectedDims0 = { 64, 64, 16, 3, 4 }; + long[] expectedDims1 = { 32, 32, 8, 3, 4 }; assertArrayEquals( expectedDims0, ijLevel0.getImgPlus().dimensionsAsLongArray(), "Dimensions of level 0 should be [x=64, y=64, z=16, c=3, t=4]" ); From bc40c4d13c9e401a6e62601ab90ae80a41cf5d63 Mon Sep 17 00:00:00 2001 From: hahmann Date: Fri, 23 Oct 2026 15:33:54 +0200 Subject: [PATCH 25/25] Test ActiveViewerService.viewerOf Co-Authored-By: Claude Opus 5.5 (1M context) --- .../fiji/plugins/ActiveViewerServiceTest.java | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 ome-zarr-fiji/src/test/java/ome/zarr/fiji/plugins/ActiveViewerServiceTest.java diff --git a/ome-zarr-fiji/src/test/java/ome/zarr/fiji/plugins/ActiveViewerServiceTest.java b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/plugins/ActiveViewerServiceTest.java new file mode 100644 index 000000000..405faa4e0 --- /dev/null +++ b/ome-zarr-fiji/src/test/java/ome/zarr/fiji/plugins/ActiveViewerServiceTest.java @@ -0,0 +1,161 @@ +/*- + * #%L + * OME-Zarr extras for Fiji + * %% + * Copyright (C) 2022 - 2026 SciJava developers + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package ome.zarr.fiji.plugins; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import java.awt.GridLayout; +import java.util.Collections; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JTable; + +import net.imagej.patcher.LegacyInjector; + +import org.junit.jupiter.api.Test; + +import bdv.cache.CacheControl; +import bdv.viewer.ViewerPanel; +import ij.IJ; +import ij.ImagePlus; + +/** + * Which image viewer a focus owner belongs to, as seen in Fiji with a focus probe: ImageJ + * focuses the {@code ImageWindow} or its canvas, BDV its display canvas or, in the side panel, + * the source table or group tree, which lie outside the {@link ViewerPanel}. + */ +class ActiveViewerServiceTest +{ + static + { + // Patch IJ1 before ij.* classes are loaded, as ActiveViewerService does. + LegacyInjector.preinit(); + } + + @Test + void focusInsideABdvViewerPanelGivesThatPanel() + { + final ViewerPanel viewerPanel = bdvViewerPanel(); + try + { + assertSame( viewerPanel, ActiveViewerService.viewerOf( viewerPanel.getDisplayComponent() ) ); + } + finally + { + viewerPanel.stop(); + } + } + + @Test + void focusInTheSidePanelGivesTheWindowsOnlyBdvViewerPanel() + { + final ViewerPanel viewerPanel = bdvViewerPanel(); + final JTable sourceTable = new JTable(); + final JFrame frame = frameWith( viewerPanel, sourceTable ); + try + { + assertSame( viewerPanel, ActiveViewerService.viewerOf( sourceTable ) ); + } + finally + { + viewerPanel.stop(); + frame.dispose(); + } + } + + @Test + void focusBesideTwoBdvViewerPanelsGivesNone() + { + final ViewerPanel left = bdvViewerPanel(); + final ViewerPanel right = bdvViewerPanel(); + final JTable sourceTable = new JTable(); + final JFrame frame = frameWith( left, right, sourceTable ); + try + { + assertNull( ActiveViewerService.viewerOf( sourceTable ) ); + assertSame( right, ActiveViewerService.viewerOf( right.getDisplayComponent() ) ); + } + finally + { + left.stop(); + right.stop(); + frame.dispose(); + } + } + + @Test + void focusOutsideAnyImageViewerGivesNone() + { + final JButton button = new JButton(); + final JFrame frame = frameWith( button ); + try + { + assertNull( ActiveViewerService.viewerOf( button ) ); + assertNull( ActiveViewerService.viewerOf( frame ) ); + assertNull( ActiveViewerService.viewerOf( null ) ); + } + finally + { + frame.dispose(); + } + } + + @Test + void focusInAnImageJWindowGivesThatWindow() + { + final ImagePlus imagePlus = IJ.createImage( "Demo", "8-bit ramp", 64, 64, 1 ); + imagePlus.show(); + try + { + assertSame( imagePlus.getWindow(), ActiveViewerService.viewerOf( imagePlus.getWindow() ) ); + assertSame( imagePlus.getWindow(), ActiveViewerService.viewerOf( imagePlus.getCanvas() ) ); + } + finally + { + imagePlus.close(); + } + } + + private static ViewerPanel bdvViewerPanel() + { + return new ViewerPanel( Collections.emptyList(), 1, new CacheControl.Dummy() ); + } + + /** A window holding {@code components}, never shown: {@code viewerOf} only walks the component tree. */ + private static JFrame frameWith( final java.awt.Component... components ) + { + final JFrame frame = new JFrame(); + frame.getContentPane().setLayout( new GridLayout( 1, components.length ) ); + for ( final java.awt.Component component : components ) + frame.getContentPane().add( component ); + return frame; + } +}