docs(sash): 📝 add documentation for the Sash widget
Related-to: #124652
This commit is contained in:
parent
2b03df8938
commit
e7ea07d358
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -86,6 +86,7 @@
|
||||||
"splitview",
|
"splitview",
|
||||||
"table",
|
"table",
|
||||||
"list",
|
"list",
|
||||||
"git"
|
"git",
|
||||||
|
"sash"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,29 +13,39 @@ import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecy
|
||||||
import { isMacintosh } from 'vs/base/common/platform';
|
import { isMacintosh } from 'vs/base/common/platform';
|
||||||
import 'vs/css!./sash';
|
import 'vs/css!./sash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow the sashes to be visible at runtime.
|
||||||
|
* @remark Use for development purposes only.
|
||||||
|
*/
|
||||||
let DEBUG = false;
|
let DEBUG = false;
|
||||||
// DEBUG = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this
|
// DEBUG = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this
|
||||||
|
|
||||||
export interface ISashLayoutProvider { }
|
/**
|
||||||
|
* A vertical sash layout provider provides position and height for a sash.
|
||||||
export interface IVerticalSashLayoutProvider extends ISashLayoutProvider {
|
*/
|
||||||
|
export interface IVerticalSashLayoutProvider {
|
||||||
getVerticalSashLeft(sash: Sash): number;
|
getVerticalSashLeft(sash: Sash): number;
|
||||||
getVerticalSashTop?(sash: Sash): number;
|
getVerticalSashTop?(sash: Sash): number;
|
||||||
getVerticalSashHeight?(sash: Sash): number;
|
getVerticalSashHeight?(sash: Sash): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IHorizontalSashLayoutProvider extends ISashLayoutProvider {
|
/**
|
||||||
|
* A vertical sash layout provider provides position and width for a sash.
|
||||||
|
*/
|
||||||
|
export interface IHorizontalSashLayoutProvider {
|
||||||
getHorizontalSashTop(sash: Sash): number;
|
getHorizontalSashTop(sash: Sash): number;
|
||||||
getHorizontalSashLeft?(sash: Sash): number;
|
getHorizontalSashLeft?(sash: Sash): number;
|
||||||
getHorizontalSashWidth?(sash: Sash): number;
|
getHorizontalSashWidth?(sash: Sash): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ISashLayoutProvider = IVerticalSashLayoutProvider | IHorizontalSashLayoutProvider;
|
||||||
|
|
||||||
export interface ISashEvent {
|
export interface ISashEvent {
|
||||||
startX: number;
|
readonly startX: number;
|
||||||
currentX: number;
|
readonly currentX: number;
|
||||||
startY: number;
|
readonly startY: number;
|
||||||
currentY: number;
|
readonly currentY: number;
|
||||||
altKey: boolean;
|
readonly altKey: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum OrthogonalEdge {
|
export enum OrthogonalEdge {
|
||||||
|
@ -46,10 +56,41 @@ export enum OrthogonalEdge {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISashOptions {
|
export interface ISashOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a sash is horizontal or vertical.
|
||||||
|
*/
|
||||||
readonly orientation: Orientation;
|
readonly orientation: Orientation;
|
||||||
readonly orthogonalStartSash?: Sash;
|
|
||||||
readonly orthogonalEndSash?: Sash;
|
/**
|
||||||
|
* The width or height of a vertical or horizontal sash, respectively.
|
||||||
|
*/
|
||||||
readonly size?: number;
|
readonly size?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference to another sash, perpendicular to this one, which
|
||||||
|
* aligns at the start of this one. A corner sash will be created
|
||||||
|
* automatically at that location.
|
||||||
|
*
|
||||||
|
* The start of a horizontal sash is its left-most position.
|
||||||
|
* The start of a vertical sash is its top-most position.
|
||||||
|
*/
|
||||||
|
readonly orthogonalStartSash?: Sash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference to another sash, perpendicular to this one, which
|
||||||
|
* aligns at the end of this one. A corner sash will be created
|
||||||
|
* automatically at that location.
|
||||||
|
*
|
||||||
|
* The end of a horizontal sash is its right-most position.
|
||||||
|
* The end of a vertical sash is its bottom-most position.
|
||||||
|
*/
|
||||||
|
readonly orthogonalEndSash?: Sash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a hint as to what mouse cursor to use whenever the user
|
||||||
|
* hovers over a corner sash provided by this and an orthogonal sash.
|
||||||
|
*/
|
||||||
readonly orthogonalEdge?: OrthogonalEdge;
|
readonly orthogonalEdge?: OrthogonalEdge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,9 +108,31 @@ export const enum Orientation {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum SashState {
|
export const enum SashState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable any UI interaction.
|
||||||
|
*/
|
||||||
Disabled,
|
Disabled,
|
||||||
Minimum,
|
|
||||||
Maximum,
|
/**
|
||||||
|
* Allow dragging down or to the right, depending on the sash orientation.
|
||||||
|
*
|
||||||
|
* Some OSs allow customizing the mouse cursor differently whenever
|
||||||
|
* some resizable component can't be any smaller, but can be larger.
|
||||||
|
*/
|
||||||
|
AtMinimum,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow dragging up or to the left, depending on the sash orientation.
|
||||||
|
*
|
||||||
|
* Some OSs allow customizing the mouse cursor differently whenever
|
||||||
|
* some resizable component can't be any larger, but can be smaller.
|
||||||
|
*/
|
||||||
|
AtMaximum,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable dragging.
|
||||||
|
*/
|
||||||
Enabled
|
Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,52 +222,101 @@ class OrthogonalPointerEventFactory implements IPointerEventFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Sash} is the UI component which allows the user to resize other
|
||||||
|
* components. It's usually an invisible horizontal or vertical line which, when
|
||||||
|
* hovered, becomes highlighted and can be dragged along the perpendicular dimension
|
||||||
|
* to its direction.
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Touch event handling
|
||||||
|
* - Corner sash support
|
||||||
|
* - Hover with different mouse cursor support
|
||||||
|
* - Configurable hover size
|
||||||
|
* - Linked sash support, for 2x2 corner sashes
|
||||||
|
*/
|
||||||
export class Sash extends Disposable {
|
export class Sash extends Disposable {
|
||||||
|
|
||||||
private el: HTMLElement;
|
private el: HTMLElement;
|
||||||
private layoutProvider: ISashLayoutProvider;
|
private layoutProvider: ISashLayoutProvider;
|
||||||
private orientation!: Orientation;
|
private orientation: Orientation;
|
||||||
private size: number;
|
private size: number;
|
||||||
private hoverDelay = globalHoverDelay;
|
private hoverDelay = globalHoverDelay;
|
||||||
private hoverDelayer = this._register(new Delayer(this.hoverDelay));
|
private hoverDelayer = this._register(new Delayer(this.hoverDelay));
|
||||||
|
|
||||||
private _state: SashState = SashState.Enabled;
|
private _state: SashState = SashState.Enabled;
|
||||||
|
private readonly onDidEnablementChange = this._register(new Emitter<SashState>());
|
||||||
|
private readonly _onDidStart = this._register(new Emitter<ISashEvent>());
|
||||||
|
private readonly _onDidChange = this._register(new Emitter<ISashEvent>());
|
||||||
|
private readonly _onDidReset = this._register(new Emitter<void>());
|
||||||
|
private readonly _onDidEnd = this._register(new Emitter<void>());
|
||||||
|
private readonly orthogonalStartSashDisposables = this._register(new DisposableStore());
|
||||||
|
private _orthogonalStartSash: Sash | undefined;
|
||||||
|
private readonly orthogonalStartDragHandleDisposables = this._register(new DisposableStore());
|
||||||
|
private _orthogonalStartDragHandle: HTMLElement | undefined;
|
||||||
|
private readonly orthogonalEndSashDisposables = this._register(new DisposableStore());
|
||||||
|
private _orthogonalEndSash: Sash | undefined;
|
||||||
|
private readonly orthogonalEndDragHandleDisposables = this._register(new DisposableStore());
|
||||||
|
private _orthogonalEndDragHandle: HTMLElement | undefined;
|
||||||
|
|
||||||
get state(): SashState { return this._state; }
|
get state(): SashState { return this._state; }
|
||||||
|
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
|
||||||
|
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of a sash defines whether it can be interacted with by the user
|
||||||
|
* as well as what mouse cursor to use, when hovered.
|
||||||
|
*/
|
||||||
set state(state: SashState) {
|
set state(state: SashState) {
|
||||||
if (this._state === state) {
|
if (this._state === state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.el.classList.toggle('disabled', state === SashState.Disabled);
|
this.el.classList.toggle('disabled', state === SashState.Disabled);
|
||||||
this.el.classList.toggle('minimum', state === SashState.Minimum);
|
this.el.classList.toggle('minimum', state === SashState.AtMinimum);
|
||||||
this.el.classList.toggle('maximum', state === SashState.Maximum);
|
this.el.classList.toggle('maximum', state === SashState.AtMaximum);
|
||||||
|
|
||||||
this._state = state;
|
this._state = state;
|
||||||
this._onDidEnablementChange.fire(state);
|
this.onDidEnablementChange.fire(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _onDidEnablementChange = this._register(new Emitter<SashState>());
|
/**
|
||||||
readonly onDidEnablementChange: Event<SashState> = this._onDidEnablementChange.event;
|
* An event which fires whenever the user starts dragging this sash.
|
||||||
|
*/
|
||||||
private readonly _onDidStart = this._register(new Emitter<ISashEvent>());
|
|
||||||
readonly onDidStart: Event<ISashEvent> = this._onDidStart.event;
|
readonly onDidStart: Event<ISashEvent> = this._onDidStart.event;
|
||||||
|
|
||||||
private readonly _onDidChange = this._register(new Emitter<ISashEvent>());
|
/**
|
||||||
|
* An event which fires whenever the user moves the mouse while
|
||||||
|
* dragging this sash.
|
||||||
|
*/
|
||||||
readonly onDidChange: Event<ISashEvent> = this._onDidChange.event;
|
readonly onDidChange: Event<ISashEvent> = this._onDidChange.event;
|
||||||
|
|
||||||
private readonly _onDidReset = this._register(new Emitter<void>());
|
/**
|
||||||
|
* An event which fires whenever the user double clicks this sash.
|
||||||
|
*/
|
||||||
readonly onDidReset: Event<void> = this._onDidReset.event;
|
readonly onDidReset: Event<void> = this._onDidReset.event;
|
||||||
|
|
||||||
private readonly _onDidEnd = this._register(new Emitter<void>());
|
/**
|
||||||
|
* An event which fires whenever the user stops dragging this sash.
|
||||||
|
*/
|
||||||
readonly onDidEnd: Event<void> = this._onDidEnd.event;
|
readonly onDidEnd: Event<void> = this._onDidEnd.event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A linked sash will be forwarded the same user interactions and events
|
||||||
|
* so it moves exactly the same way as this sash.
|
||||||
|
*
|
||||||
|
* Useful in 2x2 grids. Not meant for widespread usage.
|
||||||
|
*/
|
||||||
linkedSash: Sash | undefined = undefined;
|
linkedSash: Sash | undefined = undefined;
|
||||||
|
|
||||||
private readonly orthogonalStartSashDisposables = this._register(new DisposableStore());
|
/**
|
||||||
private _orthogonalStartSash: Sash | undefined;
|
* A reference to another sash, perpendicular to this one, which
|
||||||
private readonly orthogonalStartDragHandleDisposables = this._register(new DisposableStore());
|
* aligns at the start of this one. A corner sash will be created
|
||||||
private _orthogonalStartDragHandle: HTMLElement | undefined;
|
* automatically at that location.
|
||||||
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
|
*
|
||||||
|
* The start of a horizontal sash is its left-most position.
|
||||||
|
* The start of a vertical sash is its top-most position.
|
||||||
|
*/
|
||||||
set orthogonalStartSash(sash: Sash | undefined) {
|
set orthogonalStartSash(sash: Sash | undefined) {
|
||||||
this.orthogonalStartDragHandleDisposables.clear();
|
this.orthogonalStartDragHandleDisposables.clear();
|
||||||
this.orthogonalStartSashDisposables.clear();
|
this.orthogonalStartSashDisposables.clear();
|
||||||
|
@ -223,18 +335,22 @@ export class Sash extends Disposable {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange(onChange, this));
|
this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange.event(onChange, this));
|
||||||
onChange(sash.state);
|
onChange(sash.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._orthogonalStartSash = sash;
|
this._orthogonalStartSash = sash;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly orthogonalEndSashDisposables = this._register(new DisposableStore());
|
/**
|
||||||
private _orthogonalEndSash: Sash | undefined;
|
* A reference to another sash, perpendicular to this one, which
|
||||||
private readonly orthogonalEndDragHandleDisposables = this._register(new DisposableStore());
|
* aligns at the end of this one. A corner sash will be created
|
||||||
private _orthogonalEndDragHandle: HTMLElement | undefined;
|
* automatically at that location.
|
||||||
get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; }
|
*
|
||||||
|
* The end of a horizontal sash is its right-most position.
|
||||||
|
* The end of a vertical sash is its bottom-most position.
|
||||||
|
*/
|
||||||
|
|
||||||
set orthogonalEndSash(sash: Sash | undefined) {
|
set orthogonalEndSash(sash: Sash | undefined) {
|
||||||
this.orthogonalEndDragHandleDisposables.clear();
|
this.orthogonalEndDragHandleDisposables.clear();
|
||||||
this.orthogonalEndSashDisposables.clear();
|
this.orthogonalEndSashDisposables.clear();
|
||||||
|
@ -253,15 +369,30 @@ export class Sash extends Disposable {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange(onChange, this));
|
this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange.event(onChange, this));
|
||||||
onChange(sash.state);
|
onChange(sash.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._orthogonalEndSash = sash;
|
this._orthogonalEndSash = sash;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(container: HTMLElement, layoutProvider: IVerticalSashLayoutProvider, options: ISashOptions);
|
/**
|
||||||
constructor(container: HTMLElement, layoutProvider: IHorizontalSashLayoutProvider, options: ISashOptions);
|
* Create a new vertical sash.
|
||||||
|
*
|
||||||
|
* @param container A DOM node to append the sash to.
|
||||||
|
* @param verticalLayoutProvider A vertical layout provider.
|
||||||
|
* @param options The options.
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement, verticalLayoutProvider: IVerticalSashLayoutProvider, options: IVerticalSashOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new horizontal sash.
|
||||||
|
*
|
||||||
|
* @param container A DOM node to append the sash to.
|
||||||
|
* @param horizontalLayoutProvider A horizontal layout provider.
|
||||||
|
* @param options The options.
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement, horizontalLayoutProvider: IHorizontalSashLayoutProvider, options: IHorizontalSashOptions);
|
||||||
constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions) {
|
constructor(container: HTMLElement, layoutProvider: ISashLayoutProvider, options: ISashOptions) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -381,17 +512,17 @@ export class Sash extends Disposable {
|
||||||
if (isMultisashResize) {
|
if (isMultisashResize) {
|
||||||
cursor = 'all-scroll';
|
cursor = 'all-scroll';
|
||||||
} else if (this.orientation === Orientation.HORIZONTAL) {
|
} else if (this.orientation === Orientation.HORIZONTAL) {
|
||||||
if (this.state === SashState.Minimum) {
|
if (this.state === SashState.AtMinimum) {
|
||||||
cursor = 's-resize';
|
cursor = 's-resize';
|
||||||
} else if (this.state === SashState.Maximum) {
|
} else if (this.state === SashState.AtMaximum) {
|
||||||
cursor = 'n-resize';
|
cursor = 'n-resize';
|
||||||
} else {
|
} else {
|
||||||
cursor = isMacintosh ? 'row-resize' : 'ns-resize';
|
cursor = isMacintosh ? 'row-resize' : 'ns-resize';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.state === SashState.Minimum) {
|
if (this.state === SashState.AtMinimum) {
|
||||||
cursor = 'e-resize';
|
cursor = 'e-resize';
|
||||||
} else if (this.state === SashState.Maximum) {
|
} else if (this.state === SashState.AtMaximum) {
|
||||||
cursor = 'w-resize';
|
cursor = 'w-resize';
|
||||||
} else {
|
} else {
|
||||||
cursor = isMacintosh ? 'col-resize' : 'ew-resize';
|
cursor = isMacintosh ? 'col-resize' : 'ew-resize';
|
||||||
|
@ -406,7 +537,7 @@ export class Sash extends Disposable {
|
||||||
updateStyle();
|
updateStyle();
|
||||||
|
|
||||||
if (!isMultisashResize) {
|
if (!isMultisashResize) {
|
||||||
this.onDidEnablementChange(updateStyle, null, disposables);
|
this.onDidEnablementChange.event(updateStyle, null, disposables);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPointerMove = (e: PointerEvent) => {
|
const onPointerMove = (e: PointerEvent) => {
|
||||||
|
@ -472,10 +603,19 @@ export class Sash extends Disposable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcefully stop any user interactions with this sash.
|
||||||
|
* Useful when hiding a parent component, while the user is still
|
||||||
|
* interacting with the sash.
|
||||||
|
*/
|
||||||
clearSashHoverState(): void {
|
clearSashHoverState(): void {
|
||||||
Sash.onMouseLeave(this);
|
Sash.onMouseLeave(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout the sash. The sash will size and position itself
|
||||||
|
* based on its provided {@link ISashLayoutProvider layout provider}.
|
||||||
|
*/
|
||||||
layout(): void {
|
layout(): void {
|
||||||
if (this.orientation === Orientation.VERTICAL) {
|
if (this.orientation === Orientation.VERTICAL) {
|
||||||
const verticalProvider = (<IVerticalSashLayoutProvider>this.layoutProvider);
|
const verticalProvider = (<IVerticalSashLayoutProvider>this.layoutProvider);
|
||||||
|
|
|
@ -964,16 +964,16 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
|
||||||
const snappedAfter = typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible;
|
const snappedAfter = typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible;
|
||||||
|
|
||||||
if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) {
|
if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) {
|
||||||
sash.state = SashState.Minimum;
|
sash.state = SashState.AtMinimum;
|
||||||
} else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) {
|
} else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) {
|
||||||
sash.state = SashState.Maximum;
|
sash.state = SashState.AtMaximum;
|
||||||
} else {
|
} else {
|
||||||
sash.state = SashState.Disabled;
|
sash.state = SashState.Disabled;
|
||||||
}
|
}
|
||||||
} else if (min && !max) {
|
} else if (min && !max) {
|
||||||
sash.state = SashState.Minimum;
|
sash.state = SashState.AtMinimum;
|
||||||
} else if (!min && max) {
|
} else if (!min && max) {
|
||||||
sash.state = SashState.Maximum;
|
sash.state = SashState.AtMaximum;
|
||||||
} else {
|
} else {
|
||||||
sash.state = SashState.Enabled;
|
sash.state = SashState.Enabled;
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,12 +297,12 @@ suite('Splitview', () => {
|
||||||
assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');
|
assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');
|
||||||
|
|
||||||
view1.maximumSize = 300;
|
view1.maximumSize = 300;
|
||||||
assert.strictEqual(sashes[0].state, SashState.Minimum, 'first sash is enabled');
|
assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled');
|
||||||
assert.strictEqual(sashes[1].state, SashState.Minimum, 'second sash is enabled');
|
assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled');
|
||||||
|
|
||||||
view2.maximumSize = 200;
|
view2.maximumSize = 200;
|
||||||
assert.strictEqual(sashes[0].state, SashState.Minimum, 'first sash is enabled');
|
assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled');
|
||||||
assert.strictEqual(sashes[1].state, SashState.Minimum, 'second sash is enabled');
|
assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled');
|
||||||
|
|
||||||
splitview.resizeView(0, 40);
|
splitview.resizeView(0, 40);
|
||||||
assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');
|
assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');
|
||||||
|
|
Loading…
Reference in a new issue