Skip to content

Commit 548be2b

Browse files
Merge pull request #43 from HaudinFlorence/replace_contextual_menu_on_the_bluetooth_device_running_items_by_a_menu
Replace contextual menu on the bluetooth device running item by a menu
2 parents a8c8f5c + 1d05396 commit 548be2b

7 files changed

Lines changed: 120 additions & 116 deletions

File tree

examples/introduction.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@
397397
"name": "python",
398398
"nbconvert_exporter": "python",
399399
"pygments_lexer": "ipython3",
400-
"version": "3.13.1"
400+
"version": "3.13.2"
401401
}
402402
},
403403
"nbformat": 4,

src/bluetooth-extension/index.ts

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -69,34 +69,20 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin<void> = {
6969
);
7070
let runningItemsList: Array<IRunningSessions.IRunningItem>;
7171

72-
function createTestFunction(device: BluetoothManager.Device) {
73-
return function test(node: HTMLElement): boolean {
74-
const testString = buildCompleteIdentifier(device.native);
75-
return node.title === testString;
76-
};
77-
}
78-
79-
commands.addCommand(CommandIDs.disconnectDevice, {
80-
execute: async args => {
81-
bluetoothManager.deviceList.filter(item => {
82-
const testWithDevice = createTestFunction(item);
83-
const node = app.contextMenuHitTest(testWithDevice);
84-
const identifier = buildCompleteIdentifier(item.native);
85-
if (identifier === node?.title) {
86-
bluetoothManager.disconnectDevice(item);
87-
}
88-
});
72+
app.commands.addCommand(CommandIDs.disconnectDevice, {
73+
execute: (args) => {
74+
const selectedDevice= bluetoothManager.deviceList.find((device) => device.native.id === args.deviceID as string);
75+
if (selectedDevice) {
76+
bluetoothManager.disconnectDevice(selectedDevice);
77+
return selectedDevice;
78+
} else {
79+
throw new Error('No device provided or device is invalid');
80+
}
8981
},
9082
caption: trans.__('Disconnect device'),
9183
label: trans.__('Disconnect Device')
9284
});
9385

94-
/* Adding commands to the context menu of the relevant connected device*/
95-
app.contextMenu.addItem({
96-
command: CommandIDs.disconnectDevice,
97-
selector: 'jp-tree-item.jp-RunningSessions-item.jp-bluetooth-Move-Hub',
98-
rank: 0
99-
});
10086

10187
app.commands.addCommand(CommandIDs.openDeviceRegistryDialog, {
10288
execute: async () => {
@@ -121,6 +107,7 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin<void> = {
121107
}
122108
});
123109

110+
124111
managers.add({
125112
name: trans.__('Bluetooth Devices'),
126113
supportsMultipleViews: false,
@@ -130,10 +117,12 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin<void> = {
130117
runningItemsList.push(
131118
new BluetoothDeviceRunningItem(
132119
device,
133-
bluetoothManager as BluetoothManager
120+
bluetoothManager as BluetoothManager,
121+
commands
134122
)
135123
);
136-
});
124+
}
125+
);
137126
return runningItemsList;
138127
},
139128
shutdownAll: () => {
@@ -163,8 +152,7 @@ const BluetoothSidebarPlugin: JupyterFrontEndPlugin<void> = {
163152

164153
export class DropDownRegistry
165154
extends Widget
166-
implements Dialog.IBodyWidget<string>
167-
{
155+
implements Dialog.IBodyWidget<string> {
168156
constructor(registry: BluetoothManager.DeviceRegistry) {
169157
super();
170158
this._selectList = document.createElement('select');

src/bluetooth/BluetoothDeviceRunningItem.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,42 @@ import { IRunningSessions } from '@jupyterlab/running';
22
import { BluetoothConnectIcon } from './icon';
33
import { BluetoothManager } from './BluetoothManager';
44
import { buildCompleteIdentifier } from '../bluetooth-extension';
5+
import { Menu } from '@lumino/widgets';
6+
import { CommandRegistry } from '@lumino/commands';
7+
8+
export const disconnectDevice = 'bluetooth-manager:disconnect-device';
59

610
export class BluetoothDeviceRunningItem
7-
implements IRunningSessions.IRunningItem
8-
{
9-
constructor(device: BluetoothManager.Device, manager: BluetoothManager) {
11+
implements IRunningSessions.IRunningItem {
12+
constructor(device: BluetoothManager.Device, bluetoothManager: BluetoothManager, commands: CommandRegistry) {
1013
this._device = device;
11-
this.manager = manager;
12-
14+
this.bluetoothManager = bluetoothManager;
1315
if (this._device.native.name) {
1416
const deviceName = this._device.native.name;
1517
this.className = 'jp-bluetooth-' + deviceName.replace(/\s+/g, '-');
1618
}
19+
this.commands = commands;
1720
}
1821

1922
className?: string | undefined;
2023

24+
open() {
25+
const commands = this.commands;
26+
const deviceID = this._device.native.id;
27+
const menu = new Menu({ commands: commands })
28+
this._device.contextCommands.map((command: string) => {
29+
menu.addItem({ command: command, args: {deviceID}})
30+
})
31+
menu.addClass('jp-bluetooth-device-running-item-menu')
32+
const deviceElement = document.querySelector(`.${this.className}`);
33+
if (deviceElement) {
34+
const rect = deviceElement.getBoundingClientRect();
35+
const x = rect.left;
36+
const y = rect.bottom;
37+
menu.open(x, y);
38+
}
39+
}
40+
2141
icon() {
2242
return BluetoothConnectIcon;
2343
}
@@ -33,9 +53,10 @@ export class BluetoothDeviceRunningItem
3353
}
3454

3555
shutdown() {
36-
this.manager.disconnectDevice(this._device);
56+
this.bluetoothManager.disconnectDevice(this._device);
3757
}
3858

3959
private _device: BluetoothManager.Device;
40-
public manager: BluetoothManager;
60+
public bluetoothManager: BluetoothManager;
61+
public commands: CommandRegistry;
4162
}

src/bluetooth/BluetoothManager.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,15 @@ export namespace BluetoothManager {
147147
public connected: Signal<this, boolean>;
148148
public disconnected: Signal<this, boolean>;
149149
public isDisposed: boolean;
150+
public contextCommands:Array<string>;
150151

151152
constructor(native: BluetoothDevice) {
152153
this.connected = new Signal<this, boolean>(this);
153154
this.disconnected = new Signal<this, boolean>(this);
154155
this.isConnected = false;
155156
this.isDisposed = false;
156157
this.native = native;
158+
this.contextCommands = ['bluetooth-manager:disconnect-device', 'bluetooth-manager:add-lego-movehub-control-panel']
157159
}
158160

159161
async connectAndGetAllServices(): Promise<

src/movehub-extension/components/ConnectionStatus.tsx

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { CommandRegistry } from '@lumino/commands';
1010
import { BluetoothManager } from '../../bluetooth/BluetoothManager';
1111
export const connectMoveHub = 'bluetooth-manager:connect-movehub';
1212
export const disconnectMoveHub = 'bluetooth-manager:disconnect-movehub';
13-
import { movehubRegistryItem } from '..';
13+
1414

1515
export default function ConnectionStatus({ device }: IMoveHubPanelProps) {
1616
const [deviceState, setDeviceState] = useState<DeviceInfo>(defaultDeviceInfo);
@@ -46,54 +46,25 @@ export default function ConnectionStatus({ device }: IMoveHubPanelProps) {
4646
export class ConnectionStatusWidget extends ReactWidget {
4747
public device: MoveHub;
4848
public menu: Menu;
49-
public commands: CommandRegistry;
5049

51-
constructor(device: MoveHub, bluetoothManager: BluetoothManager) {
50+
constructor(device: MoveHub, bluetoothManager: BluetoothManager, commands: CommandRegistry) {
5251
super();
5352
this.device = device;
54-
this.commands = new CommandRegistry();
55-
this.commands.addCommand(disconnectMoveHub, {
56-
execute: args => {
57-
bluetoothManager.disconnectDevice(device);
58-
return device;
59-
},
60-
caption: 'Disconnect MoveHub',
61-
label: 'Disconnect MoveHub',
62-
isEnabled: () => {
63-
if (device.deviceInfo.connected) {
64-
return true;
65-
} else {
66-
return false;
67-
}
68-
}
69-
});
70-
this.commands.addCommand(connectMoveHub, {
71-
execute: args => {
72-
const newDevice = bluetoothManager.connectDevice(movehubRegistryItem);
73-
return newDevice;
74-
},
75-
caption: 'Connect MoveHub',
76-
label: 'Connect MoveHub',
77-
isEnabled: () => {
78-
if (device.deviceInfo.connected) {
79-
return false;
80-
} else {
81-
return true;
82-
}
83-
}
84-
});
85-
this.menu = new Menu({ commands: this.commands });
53+
const deviceID = this.device.native.id;
54+
55+
this.menu = new Menu({ commands: commands });
8656
this.menu.addItem({
87-
command: disconnectMoveHub
57+
command: disconnectMoveHub,
58+
args: {deviceID}
8859
});
8960
this.menu.addItem({
90-
command: connectMoveHub
61+
command: connectMoveHub,
62+
args: {deviceID}
9163
});
9264
this.menu.addClass('jp-connection-status-menu');
9365
}
9466

9567
openMenu(event: React.MouseEvent<HTMLDivElement>) {
96-
console.log('You have clicked');
9768
if (this.menu && typeof this.menu.isVisible !== 'undefined') {
9869
this.menu.open(event.clientX, event.clientY);
9970
} else {

src/movehub-extension/index.ts

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export * from './version';
2222
export * from './widget';
2323
export const addLEGOMoveHubControlPanel =
2424
'bluetooth-manager:add-lego-movehub-control-panel';
25+
export const connectMoveHub = 'bluetooth-manager:connect-movehub';
26+
export const disconnectMoveHub = 'bluetooth-manager:disconnect-movehub';
2527
export const moveHubServiceUUID = '00001623-1212-efde-1623-785feabcd123';
2628
export const moveHubCharacteristicUUID = '00001624-1212-efde-1623-785feabcd123';
2729
export const movehubRegistryItem: IDeviceRegistryItem = {
@@ -81,39 +83,32 @@ const LEGOMoveHubControlPanelPlugin: JupyterFrontEndPlugin<void> = {
8183
const device = result[
8284
result.length - 1
8385
] as MoveHub; /* the last added MoveHub device*/
84-
/*let isThemeLight: boolean = themeManager.theme;
85-
themeManager.themeChanged.connect((sender: any, args: IChangedArgs<string, string | null, string>) => {
86-
const theme = args.newValue;
87-
console.log("Is the theme light?:", themeManager.isLight(theme));
88-
isThemeLight = themeManager.isLight(theme);
89-
console.log('Theme is:', theme);
90-
});*/
91-
const content = new MoveHubPanelWidget(device, themeManager);
92-
content.addClass('jp-movehub-panel-content');
93-
const toolbar = new Toolbar();
94-
toolbar.addClass('jp-movehub-panel-toolbar');
95-
const main = new MainAreaWidget({ content, toolbar });
96-
main.addClass('jp-movehub-panel-main');
97-
main.toolbar.addItem(
98-
'connection-status',
99-
new ConnectionStatusWidget(device, bluetoothManager)
100-
);
101-
main.toolbar.addItem(
102-
'select-lego-model',
103-
new LegoBuildSelectorWidget(device)
104-
);
105-
toolbar.addItem('spacer', Toolbar.createSpacerItem());
106-
main.toolbar.addItem('battery-gauge', new BatteryWidget(device, themeManager));
107-
main.toolbar.addItem(
108-
'device-identifier',
109-
new DeviceIdentifierWidget(device)
110-
);
111-
main.id = 'lego-movehub-control-panel';
112-
main.title.label = 'LEGO® Move Hub';
113-
main.title.closable = true;
114-
main.title.icon = LegoBrickIcon;
115-
app.shell.add(main, 'main');
116-
86+
const content = new MoveHubPanelWidget(device, themeManager);
87+
content.addClass('jp-movehub-panel-content');
88+
const toolbar = new Toolbar();
89+
toolbar.addClass('jp-movehub-panel-toolbar');
90+
const main = new MainAreaWidget({ content, toolbar });
91+
main.addClass('jp-movehub-panel-main');
92+
main.toolbar.addItem(
93+
'connection-status',
94+
new ConnectionStatusWidget(device, bluetoothManager, app.commands)
95+
);
96+
main.toolbar.addItem(
97+
'select-lego-model',
98+
new LegoBuildSelectorWidget(device)
99+
);
100+
toolbar.addItem('spacer', Toolbar.createSpacerItem());
101+
main.toolbar.addItem('battery-gauge', new BatteryWidget(device, themeManager));
102+
main.toolbar.addItem(
103+
'device-identifier',
104+
new DeviceIdentifierWidget(device)
105+
);
106+
main.id = 'lego-movehub-control-panel';
107+
main.title.label = 'LEGO® Move Hub';
108+
main.title.closable = true;
109+
main.title.icon = LegoBrickIcon;
110+
app.shell.add(main, 'main');
111+
117112

118113
} else {
119114
throw new Error('The device is not a Move Hub.');
@@ -123,17 +118,43 @@ const LEGOMoveHubControlPanelPlugin: JupyterFrontEndPlugin<void> = {
123118
label: trans.__('Open a LEGO® Move Hub Control Panel')
124119
});
125120

126-
app.contextMenu.addItem({
127-
command: addLEGOMoveHubControlPanel,
128-
selector:
129-
'jp-tree-item.jp-RunningSessions-item.jp-bluetooth-LEGO-Move-Hub',
130-
rank: 1
121+
app.commands.addCommand(disconnectMoveHub, {
122+
execute: args => {
123+
const selectedDevice = bluetoothManager.deviceList.find((device) => device.native.id === args.deviceID as string);
124+
if (selectedDevice && selectedDevice instanceof MoveHub) {
125+
bluetoothManager.disconnectDevice(selectedDevice);
126+
return selectedDevice;
127+
} else {
128+
throw new Error('No device provided or device is invalid');
129+
}
130+
},
131+
caption: 'Disconnect MoveHub',
132+
label: 'Disconnect MoveHub',
133+
isEnabled: (args) => {
134+
const selectedDevice = bluetoothManager.deviceList.find((device) => device.native.id === args.deviceID as string);
135+
if (selectedDevice && selectedDevice instanceof MoveHub && selectedDevice.deviceInfo.connected) {
136+
return true;
137+
} else {
138+
return false;
139+
}
140+
}
131141
});
132-
133-
app.contextMenu.addItem({
134-
command: addLEGOMoveHubControlPanel,
135-
selector: 'jp-tree-item.jp-RunningSessions-item.jp-bluetooth-Move-Hub',
136-
rank: 1
142+
143+
app.commands.addCommand(connectMoveHub, {
144+
execute: args => {
145+
const newDevice = bluetoothManager.connectDevice(movehubRegistryItem);
146+
return newDevice;
147+
},
148+
caption: 'Connect MoveHub',
149+
label: 'Connect MoveHub',
150+
isEnabled: (args) => {
151+
const selectedDevice = bluetoothManager.deviceList.find((device) => device.native.id === args.deviceID as string);
152+
if (selectedDevice && selectedDevice instanceof MoveHub && selectedDevice.deviceInfo.connected) {
153+
return false;
154+
} else {
155+
return true;
156+
}
157+
}
137158
});
138159
}
139160
};

src/movehub-extension/moveHub.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export class MoveHub extends BluetoothManager.Device {
7979
driveSpeed: DEFAULT_CONFIG.DRIVE_SPEED,
8080
turnSpeed: DEFAULT_CONFIG.TURN_SPEED
8181
};
82+
this.contextCommands = ['bluetooth-manager:disconnect-device','bluetooth-manager:add-lego-movehub-control-panel']
8283
}
8384
logDebug(message?: any, ...optionalParams: any[]): void {
8485
if (message) {

0 commit comments

Comments
 (0)