Skip to content

Commit b1a98f5

Browse files
Add a layer to a viewer with drag and drop (#115)
* Add a layer to a viewer with drag and drop * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add retires on ui-test --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 1f797f8 commit b1a98f5

File tree

8 files changed

+93
-6
lines changed

8 files changed

+93
-6
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ jobs:
130130
- name: Execute integration tests
131131
working-directory: ui-tests
132132
run: |
133-
jlpm run test
133+
jlpm run test --retries=3
134134
135135
- name: Upload Playwright Test report
136136
if: always()

glue_jupyterlab/glue_session.py

+10
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,16 @@ def _init_ydoc(self) -> None:
349349
ydoc=self._sessionYDoc,
350350
)
351351

352+
def add_viewer_layer(self, tab_name: str, viewer_name: str, data_name: str) -> None:
353+
"""Add a layer with new dataset to a viewer"""
354+
viewer = (
355+
self._viewers.get(tab_name, {}).get(viewer_name, {}).get("widget", None)
356+
)
357+
if not viewer:
358+
return
359+
data = self._data[data_name]
360+
viewer.add_data(data)
361+
352362
def add_data(self, file_path: str) -> None:
353363
"""Add a new data file to the session"""
354364
relative_path = Path(file_path).relative_to(Path(self._path).parent)

src/commands.ts

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export namespace CommandIDs {
1919
export const openControlPanel = 'glue-control:open-control-panel';
2020

2121
export const closeControlPanel = 'glue-control:close-control-panel';
22+
23+
export const addViewerLayer = 'glue-control:add-viewer-layer';
2224
}
2325

2426
export interface INewViewerArgs {

src/leftPanel/plugin.ts

+17
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,23 @@ function addCommands(
229229
controlModel.clearConfig();
230230
}
231231
});
232+
233+
commands.addCommand(CommandIDs.addViewerLayer, {
234+
execute: async args => {
235+
const kernel = controlModel.currentSessionKernel();
236+
if (kernel === undefined) {
237+
// TODO Show an error dialog
238+
return;
239+
}
240+
241+
const code = `
242+
GLUE_SESSION.add_viewer_layer("${args.tab}", "${args.viewer}", "${args.data}")
243+
`;
244+
245+
const future = kernel.requestExecute({ code }, false);
246+
await future.done;
247+
}
248+
});
232249
}
233250

234251
export const controlPanel: JupyterFrontEndPlugin<void> = {

src/viewPanel/gridStackItem.ts

+33-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Panel, Widget } from '@lumino/widgets';
22
import { Toolbar, ToolbarButton, closeIcon } from '@jupyterlab/ui-components';
3+
import { Message } from '@lumino/messaging';
34
import { ISignal, Signal } from '@lumino/signaling';
5+
import { DATASET_MIME } from '../types';
46

57
export class GridStackItem extends Panel {
68
constructor(options: GridStackItem.IOptions) {
@@ -10,12 +12,13 @@ export class GridStackItem extends Panel {
1012
this.addClass('grid-stack-item');
1113
this.addClass('glue-item');
1214

13-
const { cellIdentity, cell, itemTitle = '', pos, size } = options;
15+
const { cellIdentity, cell, itemTitle = '', pos, size, tabName } = options;
1416
this._cellOutput = cell;
1517
this.cellIdentity = cellIdentity;
1618
this._pos = pos;
1719
this._size = size;
1820
this._title = itemTitle;
21+
this._tabName = tabName;
1922

2023
const content = new Panel();
2124
content.addClass('grid-stack-item-content');
@@ -63,10 +66,35 @@ export class GridStackItem extends Panel {
6366
this._size = value;
6467
}
6568

69+
get tabName(): string {
70+
return this._tabName;
71+
}
72+
73+
set tabName(value: string) {
74+
this._tabName = value;
75+
}
6676
get changed(): ISignal<GridStackItem, GridStackItem.IChange> {
6777
return this._changed;
6878
}
6979

80+
protected onAfterAttach(msg: Message): void {
81+
super.onAfterAttach(msg);
82+
this.node.addEventListener('drop', this._ondrop.bind(this));
83+
}
84+
85+
protected onBeforeDetach(msg: Message): void {
86+
this.node.removeEventListener('drop', this._ondrop.bind(this));
87+
super.onBeforeDetach(msg);
88+
}
89+
90+
private async _ondrop(event: DragEvent) {
91+
const datasetId = event.dataTransfer?.getData(DATASET_MIME);
92+
if (!datasetId) {
93+
return;
94+
}
95+
this._changed.emit({ action: 'layer', dataLayer: datasetId });
96+
}
97+
7098
private _createToolbar(itemTitle: string): Toolbar {
7199
const toolbar = new Toolbar();
72100
toolbar.node.addEventListener('click', () => {
@@ -97,7 +125,7 @@ export class GridStackItem extends Panel {
97125
private _size: number[];
98126
private _title: string;
99127
private _cellOutput: Widget;
100-
128+
private _tabName: string;
101129
private _changed: Signal<GridStackItem, GridStackItem.IChange>;
102130
}
103131

@@ -108,9 +136,11 @@ export namespace GridStackItem {
108136
itemTitle?: string;
109137
pos: number[];
110138
size: number[];
139+
tabName: string;
111140
}
112141

113142
export interface IChange {
114-
action: 'close' | 'lock' | 'edit';
143+
action: 'close' | 'lock' | 'edit' | 'layer';
144+
dataLayer?: string;
115145
}
116146
}

src/viewPanel/sessionWidget.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,14 @@ export class SessionWidget extends BoxPanel {
9696
}
9797

9898
private async _ondrop(event: DragEvent) {
99-
const datasetId = event.dataTransfer?.getData(DATASET_MIME);
99+
const target = event.target as HTMLElement;
100+
const viewer = target.closest('.grid-stack-item.glue-item');
101+
// No-op if the target is a viewer, it will be managed by the viewer itself.
102+
if (viewer) {
103+
return;
104+
}
100105

106+
const datasetId = event.dataTransfer?.getData(DATASET_MIME);
101107
const items: IDict<string> = {
102108
Histogram: CommandIDs.new1DHistogram,
103109
'1D Profile': CommandIDs.new1DProfile,

src/viewPanel/tabLayout.ts

+21
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,21 @@ export class TabLayout extends Layout {
318318
gridItem: item as any
319319
});
320320
}
321+
322+
/**
323+
* Request to add a layer with a dataset to the viewer.
324+
*
325+
* @param item - the viewer where to add a layer.
326+
* @param dataName - the data to add to the viewer.
327+
*/
328+
private _addViewerLayer(item: GridStackItem, dataName: string): void {
329+
this._commands.execute(CommandIDs.addViewerLayer, {
330+
tab: item.tabName,
331+
viewer: item.cellIdentity,
332+
data: dataName
333+
});
334+
}
335+
321336
/**
322337
* Handle change-event messages sent to from gridstack.
323338
*/
@@ -376,6 +391,12 @@ export class TabLayout extends Layout {
376391
case 'edit':
377392
this._handleEdit(sender);
378393
break;
394+
case 'layer':
395+
if (!change.dataLayer) {
396+
return;
397+
}
398+
this._addViewerLayer(sender, change.dataLayer);
399+
break;
379400
default:
380401
break;
381402
}

src/viewPanel/tabView.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ export class TabView extends Widget {
153153
cell: out,
154154
itemTitle: itemTitle.match(/($[a-z])|[A-Z][^A-Z]+/g)?.join(' '),
155155
pos: viewerData.pos,
156-
size: viewerData.size
156+
size: viewerData.size,
157+
tabName: tabName
157158
});
158159
const cellOutput = item.cellOutput as SimplifiedOutputArea;
159160
if (this._context) {

0 commit comments

Comments
 (0)