Kaynağa Gözat

Merge pull request #523 from afshin/grid-theme-support

Support themes in grid component.
Afshin Taylor Darian 4 yıl önce
ebeveyn
işleme
c88a0bb0ad
5 değiştirilmiş dosya ile 211 ekleme ve 159 silme
  1. 4 29
      src/index.ts
  2. 146 117
      src/panels/variables/grid.ts
  3. 9 11
      src/panels/variables/index.ts
  4. 15 2
      src/sidebar.ts
  5. 37 0
      style/variables.css

+ 4 - 29
src/index.ts

@@ -334,7 +334,8 @@ const variables: JupyterFrontEndPlugin<void> = {
           content: new VariablesBodyGrid({
             model,
             commands,
-            scopes: [{ name: title, variables }]
+            scopes: [{ name: title, variables }],
+            themeManager
           })
         });
         widget.addClass('jp-DebuggerVariables');
@@ -342,23 +343,7 @@ const variables: JupyterFrontEndPlugin<void> = {
         widget.title.icon = variableIcon;
         widget.title.label = `${service.session?.connection?.name} - ${title}`;
         void tracker.add(widget);
-
         model.changed.connect(() => widget.dispose());
-
-        if (themeManager) {
-          const updateStyle = (): void => {
-            const isLight = themeManager?.theme
-              ? themeManager.isLight(themeManager.theme)
-              : true;
-            widget.content.theme = isLight ? 'light' : 'dark';
-          };
-          themeManager.themeChanged.connect(updateStyle);
-          widget.disposed.connect(() =>
-            themeManager.themeChanged.disconnect(updateStyle)
-          );
-          updateStyle();
-        }
-
         shell.add(widget, 'main', {
           mode: tracker.currentWidget ? 'split-right' : 'split-bottom'
         });
@@ -480,7 +465,8 @@ const main: JupyterFrontEndPlugin<void> = {
     const sidebar = new Debugger.Sidebar({
       service,
       callstackCommands,
-      editorServices
+      editorServices,
+      themeManager
     });
 
     if (settingRegistry) {
@@ -500,17 +486,6 @@ const main: JupyterFrontEndPlugin<void> = {
       service.sessionChanged.connect(updateSettings);
     }
 
-    if (themeManager) {
-      const updateStyle = (): void => {
-        const isLight = themeManager?.theme
-          ? themeManager.isLight(themeManager.theme)
-          : true;
-        sidebar.variables.theme = isLight ? 'light' : 'dark';
-      };
-      themeManager.themeChanged.connect(updateStyle);
-      updateStyle();
-    }
-
     service.eventMessage.connect((_, event): void => {
       commands.notifyCommandChanged();
       if (labShell && event.event === 'initialized') {

+ 146 - 117
src/panels/variables/grid.ts

@@ -1,6 +1,8 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
+import { IThemeManager } from '@jupyterlab/apputils';
+
 import { CommandRegistry } from '@lumino/commands';
 
 import {
@@ -33,15 +35,14 @@ export class VariablesBodyGrid extends Panel {
    */
   constructor(options: VariablesBodyGrid.IOptions) {
     super();
-    const { model, commands, scopes } = options;
-    this._grid = new VariablesGrid({ commands });
+    const { model, commands, scopes, themeManager } = options;
+    this._grid = new Grid({ commands, themeManager });
     this._grid.addClass('jp-DebuggerVariables-grid');
     this._model = model;
-    this._grid.dataModel.setData(scopes ?? []);
-    const updated = (model: VariablesModel): void => {
+    this._model.changed.connect((model: VariablesModel): void => {
       this._grid.dataModel.setData(model.scopes);
-    };
-    this._model.changed.connect(updated, this);
+    }, this);
+    this._grid.dataModel.setData(scopes ?? []);
     this.addWidget(this._grid);
     this.addClass('jp-DebuggerVariables-body');
   }
@@ -52,38 +53,59 @@ export class VariablesBodyGrid extends Panel {
    * @param filter The variable filter to apply.
    */
   set filter(filter: Set<string>) {
-    (this._grid.dataModel as VariableDataGridModel).filter = filter;
+    (this._grid.dataModel as GridModel).filter = filter;
     this._grid.dataModel.setData(this._model.scopes);
   }
 
+  private _grid: Grid;
+  private _model: IDebugger.Model.IVariables;
+}
+
+/**
+ * A namespace for VariableBodyGrid `statics`.
+ */
+export namespace VariablesBodyGrid {
   /**
-   * Set the theme used in JupyterLab.
-   *
-   * @param theme The theme for the datagrid.
+   * Instantiation options for `VariablesBodyGrid`.
    */
-  set theme(theme: VariablesGrid.Theme) {
-    this._grid.theme = theme;
-  }
+  export interface IOptions {
+    /**
+     * The variables model.
+     */
+    model: IDebugger.Model.IVariables;
 
-  private _grid: VariablesGrid;
-  private _model: IDebugger.Model.IVariables;
+    /**
+     * The commands registry.
+     */
+    commands: CommandRegistry;
+
+    /**
+     * The optional initial scopes data.
+     */
+    scopes?: IDebugger.IScope[];
+
+    /**
+     * An optional application theme manager to detect theme changes.
+     */
+    themeManager?: IThemeManager | null;
+  }
 }
 
 /**
  * A class wrapping the underlying variables datagrid.
  */
-export class VariablesGrid extends Panel {
+class Grid extends Panel {
   /**
    * Instantiate a new VariablesGrid.
    *
    * @param options The instantiation options for a VariablesGrid.
    */
-  constructor(options: VariablesGrid.IOptions) {
+  constructor(options: Grid.IOptions) {
     super();
-    const { commands } = options;
-    const dataModel = new VariableDataGridModel();
+    const { commands, themeManager } = options;
+    const dataModel = new GridModel();
     const grid = new DataGrid();
-    const mouseHandler = new Private.VariablesClickHandler();
+    const mouseHandler = new Private.MouseHandler();
     mouseHandler.doubleClicked.connect((_, hit) =>
       commands.execute(CommandIDs.inspectVariable, {
         variableReference: dataModel.getVariableReference(hit.row),
@@ -99,6 +121,11 @@ export class VariablesGrid extends Panel {
     grid.stretchLastColumn = true;
     grid.node.style.height = '100%';
     this._grid = grid;
+
+    // Compute the grid's styles based on the current theme.
+    if (themeManager) {
+      themeManager.themeChanged.connect(this._updateStyles, this);
+    }
     this.addWidget(grid);
   }
 
@@ -108,36 +135,63 @@ export class VariablesGrid extends Panel {
    * @param filter The variable filter to apply.
    */
   set filter(filter: Set<string>) {
-    (this._grid.dataModel as VariableDataGridModel).filter = filter;
+    (this._grid.dataModel as GridModel).filter = filter;
     this.update();
   }
 
   /**
-   * Set the theme used in JupyterLab.
+   * Get the data model for the data grid.
+   */
+  get dataModel(): GridModel {
+    return this._grid.dataModel as GridModel;
+  }
+
+  /**
+   * Handle `after-attach` messages.
    *
-   * @param theme The theme for the datagrid.
+   * @param message - The `after-attach` message.
    */
-  set theme(theme: VariablesGrid.Theme) {
-    const { dataStyle, textRender } =
-      theme === 'dark' ? Private.DARK_STYLE : Private.LIGHT_STYLE;
-    this._grid.cellRenderers.update({}, textRender);
-    this._grid.style = dataStyle;
+  protected onAfterAttach(message: any): void {
+    super.onAfterAttach(message);
+    this._updateStyles();
   }
 
   /**
-   * Get the data model for the data grid.
+   * Update the computed style for the data grid on theme change.
    */
-  get dataModel(): VariableDataGridModel {
-    return this._grid.dataModel as VariableDataGridModel;
+  private _updateStyles(): void {
+    const { style, textRenderer } = Private.computeStyle();
+    this._grid.cellRenderers.update({}, textRenderer);
+    this._grid.style = style;
   }
 
   private _grid: DataGrid;
 }
 
 /**
- * A DataGrid model for Variables.
+ * A namespace for VariablesGrid `statics`.
+ */
+namespace Grid {
+  /**
+   * Instantiation options for `VariablesGrid`.
+   */
+  export interface IOptions {
+    /**
+     * The commands registry.
+     */
+    commands: CommandRegistry;
+
+    /**
+     * An optional application theme manager to detect theme changes.
+     */
+    themeManager?: IThemeManager | null;
+  }
+}
+
+/**
+ * A data grid model for variables.
  */
-export class VariableDataGridModel extends DataModel {
+class GridModel extends DataModel {
   /**
    * Set the variable filter list.
    */
@@ -259,103 +313,78 @@ export class VariableDataGridModel extends DataModel {
   };
 }
 
-/**
- * A namespace for VariableBodyGrid `statics`.
- */
-export namespace VariablesBodyGrid {
-  /**
-   * Instantiation options for `VariablesBodyGrid`.
-   */
-  export interface IOptions {
-    /**
-     * The variables model.
-     */
-    model: IDebugger.Model.IVariables;
-    /**
-     * The commands registry.
-     */
-    commands: CommandRegistry;
-    /**
-     * The optional initial scopes data.
-     */
-    scopes?: IDebugger.IScope[];
-  }
-}
-
-/**
- * A namespace for VariablesGrid `statics`.
- */
-export namespace VariablesGrid {
-  /**
-   * The theme for the datagrid.
-   */
-  export type Theme = 'dark' | 'light';
-
-  /**
-   * Instantiation options for `VariablesGrid`.
-   */
-  export interface IOptions {
-    /**
-     * The commands registry.
-     */
-    commands: CommandRegistry;
-  }
-}
-
 /**
  * A namespace for private data.
  */
 namespace Private {
   /**
-   * The dark theme for the data grid.
+   * Create a color palette element.
    */
-  export const DARK_STYLE = {
-    dataStyle: {
-      voidColor: '#212121',
-      backgroundColor: '#111111',
-      headerBackgroundColor: '#424242',
-      gridLineColor: 'rgba(235, 235, 235, 0.15)',
-      headerGridLineColor: 'rgba(235, 235, 235, 0.25)',
-      rowBackgroundColor: (i: number): string =>
-        i % 2 === 0 ? '#212121' : '#111111',
-      selectionFillColor: '#2196f32e'
-    },
-    textRender: new TextRenderer({
-      font: '12px sans-serif',
-      textColor: '#ffffff',
-      backgroundColor: '',
-      verticalAlignment: 'center',
-      horizontalAlignment: 'left'
-    })
-  };
+  function createPalette(): HTMLDivElement {
+    const div = document.createElement('div');
+    div.className = 'jp-DebuggerVariables-colorPalette';
+    div.innerHTML = `
+      <div class="jp-mod-void"></div>
+      <div class="jp-mod-background"></div>
+      <div class="jp-mod-header-background"></div>
+      <div class="jp-mod-grid-line"></div>
+      <div class="jp-mod-header-grid-line"></div>
+      <div class="jp-mod-selection"></div>
+      <div class="jp-mod-text"></div>
+    `;
+    return div;
+  }
 
   /**
-   * The light theme for the data grid.
+   * Compute the style and renderer for a data grid.
    */
-  export const LIGHT_STYLE = {
-    dataStyle: {
-      voidColor: 'white',
-      backgroundColor: '#f5f5f5',
-      headerBackgroundColor: '#eeeeee',
-      gridLineColor: 'rgba(20, 20, 20, 0.15)',
-      headerGridLineColor: 'rgba(20, 20, 20, 0.25)',
-      rowBackgroundColor: (i: number): string =>
-        i % 2 === 0 ? 'white' : '#f5f5f5',
-      selectionFillColor: '#2196f32e'
-    },
-    textRender: new TextRenderer({
-      font: '12px sans-serif',
-      textColor: '#000000',
-      backgroundColor: '',
-      verticalAlignment: 'center',
-      horizontalAlignment: 'left'
-    })
-  };
+  export function computeStyle(): {
+    style: DataGrid.Style;
+    textRenderer: TextRenderer;
+  } {
+    const palette = createPalette();
+    document.body.appendChild(palette);
+    let node: HTMLDivElement;
+    node = palette.querySelector('.jp-mod-void');
+    const voidColor = getComputedStyle(node).color;
+    node = palette.querySelector('.jp-mod-background');
+    const backgroundColor = getComputedStyle(node).color;
+    node = palette.querySelector('.jp-mod-header-background');
+    const headerBackgroundColor = getComputedStyle(node).color;
+    node = palette.querySelector('.jp-mod-grid-line');
+    const gridLineColor = getComputedStyle(node).color;
+    node = palette.querySelector('.jp-mod-header-grid-line');
+    const headerGridLineColor = getComputedStyle(node).color;
+    node = palette.querySelector('.jp-mod-selection');
+    const selectionFillColor = getComputedStyle(node).color;
+    node = palette.querySelector('.jp-mod-text');
+    const textColor = getComputedStyle(node).color;
+    document.body.removeChild(palette);
+    return {
+      style: {
+        voidColor,
+        backgroundColor,
+        headerBackgroundColor,
+        gridLineColor,
+        headerGridLineColor,
+        rowBackgroundColor: (i: number): string =>
+          i % 2 === 0 ? voidColor : backgroundColor,
+        selectionFillColor
+      },
+      textRenderer: new TextRenderer({
+        font: '12px sans-serif',
+        textColor,
+        backgroundColor: '',
+        verticalAlignment: 'center',
+        horizontalAlignment: 'left'
+      })
+    };
+  }
 
   /**
    * A custom click handler to handle clicks on the variables grid.
    */
-  export class VariablesClickHandler extends BasicMouseHandler {
+  export class MouseHandler extends BasicMouseHandler {
     /**
      * A signal emitted when the variables grid is double clicked.
      */

+ 9 - 11
src/panels/variables/index.ts

@@ -1,7 +1,7 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
-import { ToolbarButton } from '@jupyterlab/apputils';
+import { IThemeManager, ToolbarButton } from '@jupyterlab/apputils';
 
 import { CommandRegistry } from '@lumino/commands';
 
@@ -9,7 +9,7 @@ import { Panel, Widget } from '@lumino/widgets';
 
 import { IDebugger } from '../../tokens';
 
-import { VariablesBodyGrid, VariablesGrid } from './grid';
+import { VariablesBodyGrid } from './grid';
 
 import { VariablesHeader } from './header';
 
@@ -27,11 +27,11 @@ export class Variables extends Panel {
   constructor(options: Variables.IOptions) {
     super();
 
-    const { model, service, commands } = options;
+    const { model, service, commands, themeManager } = options;
 
     this._header = new VariablesHeader();
     this._tree = new VariablesBodyTree({ model, service });
-    this._table = new VariablesBodyGrid({ model, commands });
+    this._table = new VariablesBodyGrid({ model, commands, themeManager });
     this._table.hide();
 
     const onClick = (): void => {
@@ -70,13 +70,6 @@ export class Variables extends Panel {
     this._table.filter = filter;
   }
 
-  /**
-   * Set the theme for the variable table.
-   */
-  set theme(theme: VariablesGrid.Theme) {
-    this._table.theme = theme;
-  }
-
   /**
    * A message handler invoked on a `'resize'` message.
    *
@@ -143,5 +136,10 @@ export namespace Variables {
      * The commands registry.
      */
     commands: CommandRegistry;
+
+    /**
+     * An optional application theme manager to detect theme changes.
+     */
+    themeManager?: IThemeManager | null;
   }
 }

+ 15 - 2
src/sidebar.ts

@@ -1,6 +1,8 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
+import { IThemeManager } from '@jupyterlab/apputils';
+
 import { IEditorServices } from '@jupyterlab/codeeditor';
 
 import { bugIcon } from '@jupyterlab/ui-components';
@@ -32,14 +34,20 @@ export class DebuggerSidebar extends Panel {
     this.title.iconRenderer = bugIcon;
     this.addClass('jp-DebuggerSidebar');
 
-    const { callstackCommands, editorServices, service } = options;
+    const {
+      callstackCommands,
+      editorServices,
+      service,
+      themeManager
+    } = options;
 
     const model = service.model;
 
     this.variables = new VariablesPanel({
       model: model.variables,
       commands: callstackCommands.registry,
-      service
+      service,
+      themeManager
     });
 
     this.callstack = new CallstackPanel({
@@ -134,6 +142,11 @@ export namespace DebuggerSidebar {
      * The editor services.
      */
     editorServices: IEditorServices;
+
+    /**
+     * An optional application theme manager to detect theme changes.
+     */
+    themeManager?: IThemeManager | null;
   }
 
   /**

+ 37 - 0
style/variables.css

@@ -47,3 +47,40 @@
 .jp-DebuggerVariables-grid .lm-DataGrid {
   border: none;
 }
+
+.jp-DebuggerVariables-colorPalette {
+  visibility: hidden;
+  z-index: -999;
+  position: absolute;
+  left: -999;
+  top: -999;
+}
+
+.jp-DebuggerVariables-colorPalette .jp-mod-void {
+  color: var(--jp-layout-color1);
+}
+
+.jp-DebuggerVariables-colorPalette .jp-mod-background {
+  color: var(--jp-rendermime-table-row-background);
+}
+
+.jp-DebuggerVariables-colorPalette .jp-mod-header-background {
+  color: var(--jp-layout-color2);
+}
+
+.jp-DebuggerVariables-colorPalette .jp-mod-grid-line {
+  color: var(--jp-border-color3);
+}
+
+.jp-DebuggerVariables-colorPalette .jp-mod-header-grid-line {
+  color: var(--jp-border-color3);
+}
+
+.jp-DebuggerVariables-colorPalette .jp-mod-selection {
+  /* TODO: Fix JupyterLab light theme (alpha) so this can be a variable. */
+  color: rgba(3, 169, 244, 0.2);
+}
+
+.jp-DebuggerVariables-colorPalette .jp-mod-text {
+  color: var(--jp-content-font-color0);
+}