mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-13 19:48:37 -05:00
Support execution count in run button and align correctly (#3979)
Fixes #3931 - Align run button correctly so it's centered in new cell - Refactor to support multi-state button. - Hidden state is set to show execution count - Stopped state shows run button - Running state shows stop button - Error state (will) show error button. This isn't fully handled right now - Add execution count to model and to SqlKernel, verify serialization, loading, update matches other notebook viewers **Notes on implementation**: I think this is a decent solution for a) showing execution count as text, and b) perfectly centering the run button. The below solution shows count correctly up to 999 runs (that’s clicking 999 times in a single session), the icon lines up juuust about right with [ ] but for other numbers it is pretty close but probably not exactly right. I wish I could solve this to work better but trying to change text float to center etc. really isn’t working. **Screenshots**:  With running cell: 
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: row" (mouseover)="hover=true" (mouseleave)="hover=false">
|
||||
<div #toolbar class="toolbar" style="flex: 0 0 auto; display: flex; flex-flow:column; width: 40px; min-height: 40px; orientation: portrait">
|
||||
<div #toolbar class="toolbar">
|
||||
</div>
|
||||
<div #editor class="editor" style="flex: 1 1 auto; overflow: hidden;">
|
||||
</div>
|
||||
|
||||
@@ -55,10 +55,10 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
}
|
||||
|
||||
@Input() set hover(value: boolean) {
|
||||
this._hover = value;
|
||||
this.cellModel.hover = value;
|
||||
if (!this.isActive()) {
|
||||
// Only make a change if we're not active, since this has priority
|
||||
this.toggleMoreActionsButton(this._hover);
|
||||
this.toggleMoreActionsButton(this.cellModel.hover);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
|
||||
private _model: NotebookModel;
|
||||
private _activeCellId: string;
|
||||
private _cellToggleMoreActions: CellToggleMoreActions;
|
||||
private _hover: boolean;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface,
|
||||
|
||||
@@ -11,12 +11,18 @@ code-component {
|
||||
|
||||
code-component .toolbar {
|
||||
border-right-width: 1px;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-flow:column;
|
||||
width: 40px;
|
||||
min-height: 40px;
|
||||
orientation: portrait
|
||||
}
|
||||
|
||||
code-component .toolbar .carbon-taskbar {
|
||||
position: sticky;
|
||||
top: 10px;
|
||||
bottom: 30px;
|
||||
top: 0px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
code-component .toolbarIconRun {
|
||||
@@ -30,15 +36,21 @@ code-component .toolbarIconRun {
|
||||
background-image: url('../media/dark/execute_cell_inverse.svg');
|
||||
}
|
||||
|
||||
code-component .toolbarIconRunError {
|
||||
height: 20px;
|
||||
background-image: url('../media/light/execute_cell_error.svg');
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
code-component .toolbarIconStop {
|
||||
height: 20px;
|
||||
background-image: url('../media/light/stop_cell.svg');
|
||||
background-image: url('../media/light/stop_cell_solidanimation.svg');
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.vs-dark code-component .toolbarIconStop,
|
||||
.hc-black code-component .toolbarIconStop {
|
||||
background-image: url('../media/dark/stop_cell_inverse.svg');
|
||||
background-image: url('../media/dark/stop_cell_solidanimation_inverse.svg');
|
||||
}
|
||||
|
||||
code-component .editor {
|
||||
@@ -54,6 +66,21 @@ code-component .carbon-taskbar .icon {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar .icon.hideIcon {
|
||||
width: 0px;
|
||||
padding-left: 0px;
|
||||
padding-top: 6px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar .icon.hideIcon.execCountTen {
|
||||
margin-left: -2px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar .icon.hideIcon.execCountHundred {
|
||||
margin-left: -6px;
|
||||
}
|
||||
|
||||
code-component .carbon-taskbar.monaco-toolbar .monaco-action-bar.animated .actions-container
|
||||
{
|
||||
|
||||
@@ -8,14 +8,17 @@ import { nb } from 'sqlops';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
|
||||
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
|
||||
import { getErrorMessage } from 'sql/parts/notebook/notebookUtils';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { ToggleableAction } from 'sql/parts/notebook/notebookActions';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ICellModel, CellExecutionState } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { MultiStateAction, IMultiStateData, IActionStateData } from 'sql/parts/notebook/notebookActions';
|
||||
|
||||
let notebookMoreActionMsg = localize('notebook.failed', "Please select active cell and try again");
|
||||
const emptyExecutionCountLabel = '[ ]';
|
||||
|
||||
function hasModelAndCell(context: CellContext, notificationService: INotificationService): boolean {
|
||||
if (!context || !context.model) {
|
||||
@@ -60,28 +63,21 @@ export abstract class CellActionBase extends Action {
|
||||
abstract doRun(context: CellContext): Promise<void>;
|
||||
}
|
||||
|
||||
export class RunCellAction extends ToggleableAction {
|
||||
export class RunCellAction extends MultiStateAction<CellExecutionState> {
|
||||
public static ID = 'notebook.runCell';
|
||||
public static LABEL = 'Run cell';
|
||||
private _executionChangedDisposable: IDisposable;
|
||||
private _context: CellContext;
|
||||
constructor(context: CellContext, @INotificationService private notificationService: INotificationService) {
|
||||
super(RunCellAction.ID, {
|
||||
shouldToggleTooltip: true,
|
||||
toggleOffLabel: localize('runCell', 'Run cell'),
|
||||
toggleOffClass: 'toolbarIconRun',
|
||||
toggleOnLabel: localize('stopCell', 'Cancel execution'),
|
||||
toggleOnClass: 'toolbarIconStop',
|
||||
// On == running
|
||||
isOn: false
|
||||
});
|
||||
super(RunCellAction.ID, new IMultiStateData<CellExecutionState>([
|
||||
{ key: CellExecutionState.Hidden, value: { label: emptyExecutionCountLabel, className: '', tooltip: '', hideIcon: true }},
|
||||
{ key: CellExecutionState.Stopped, value: { label: '', className: 'toolbarIconRun', tooltip: localize('runCell', 'Run cell') }},
|
||||
{ key: CellExecutionState.Running, value: { label: '', className: 'toolbarIconStop', tooltip: localize('stopCell', 'Cancel execution') }},
|
||||
{ key: CellExecutionState.Error, value: { label: '', className: 'toolbarIconRunError', tooltip: localize('errorRunCell', 'Error on last run. Click to run again') }},
|
||||
], CellExecutionState.Hidden ));
|
||||
this.ensureContextIsUpdated(context);
|
||||
}
|
||||
|
||||
private _handleExecutionStateChange(running: boolean): void {
|
||||
this.toggle(running);
|
||||
}
|
||||
|
||||
public run(context?: CellContext): TPromise<boolean> {
|
||||
return TPromise.wrap(this.doRun(context).then(() => true));
|
||||
}
|
||||
@@ -106,8 +102,30 @@ export class RunCellAction extends ToggleableAction {
|
||||
this._executionChangedDisposable.dispose();
|
||||
}
|
||||
this._context = context;
|
||||
this.toggle(context.cell.isRunning);
|
||||
this._executionChangedDisposable = this._context.cell.onExecutionStateChange(this._handleExecutionStateChange, this);
|
||||
this.updateStateAndExecutionCount(context.cell.executionState);
|
||||
this._executionChangedDisposable = this._context.cell.onExecutionStateChange((state) => {
|
||||
this.updateStateAndExecutionCount(state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateStateAndExecutionCount(state: CellExecutionState) {
|
||||
let label = emptyExecutionCountLabel;
|
||||
let className = '';
|
||||
if (!types.isUndefinedOrNull(this._context.cell.executionCount)) {
|
||||
label = `[${this._context.cell.executionCount}]`;
|
||||
// Heuristic to try and align correctly independent of execution count length. Moving left margin
|
||||
// back by a few px seems to make things "work" OK, but isn't a super clean solution
|
||||
if (label.length === 4) {
|
||||
className = 'execCountTen';
|
||||
} else if (label.length > 4) {
|
||||
className = 'execCountHundred';
|
||||
}
|
||||
}
|
||||
this.states.updateStateData(CellExecutionState.Hidden, (data) => {
|
||||
data.label = label;
|
||||
data.className = className;
|
||||
});
|
||||
this.updateState(state);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user