🤞 finalized the monitor UI

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
Akos Kitta 2019-12-05 16:35:16 +01:00
parent 29ebf055e6
commit 8c49c04359
5 changed files with 66 additions and 49 deletions

View File

@ -95,12 +95,12 @@ export class MonitorConnection {
} }
}); });
// Handles the `baudRate` changes by reconnecting if required. // Handles the `baudRate` changes by reconnecting if required.
this.monitorModel.onChange(() => { this.monitorModel.onChange(({ property }) => {
if (this.autoConnect && this.connected) { if (property === 'baudRate' && this.autoConnect && this.connected) {
const { boardsConfig } = this.boardsServiceClient; const { boardsConfig } = this.boardsServiceClient;
this.handleBoardConfigChange(boardsConfig); this.handleBoardConfigChange(boardsConfig);
} }
}) });
} }
get connected(): boolean { get connected(): boolean {

View File

@ -15,7 +15,7 @@ export class MonitorModel implements FrontendApplicationContribution {
@inject(BoardsServiceClientImpl) @inject(BoardsServiceClientImpl)
protected readonly boardsServiceClient: BoardsServiceClientImpl; protected readonly boardsServiceClient: BoardsServiceClientImpl;
protected readonly onChangeEmitter: Emitter<void>; protected readonly onChangeEmitter: Emitter<MonitorModel.State.Change<keyof MonitorModel.State>>;
protected _autoscroll: boolean; protected _autoscroll: boolean;
protected _timestamp: boolean; protected _timestamp: boolean;
protected _baudRate: MonitorConfig.BaudRate; protected _baudRate: MonitorConfig.BaudRate;
@ -26,7 +26,7 @@ export class MonitorModel implements FrontendApplicationContribution {
this._timestamp = false; this._timestamp = false;
this._baudRate = MonitorConfig.BaudRate.DEFAULT; this._baudRate = MonitorConfig.BaudRate.DEFAULT;
this._lineEnding = MonitorModel.EOL.DEFAULT; this._lineEnding = MonitorModel.EOL.DEFAULT;
this.onChangeEmitter = new Emitter<void>(); this.onChangeEmitter = new Emitter<MonitorModel.State.Change<keyof MonitorModel.State>>();
} }
onStart(): void { onStart(): void {
@ -37,7 +37,7 @@ export class MonitorModel implements FrontendApplicationContribution {
}); });
} }
get onChange(): Event<void> { get onChange(): Event<MonitorModel.State.Change<keyof MonitorModel.State>> {
return this.onChangeEmitter.event; return this.onChangeEmitter.event;
} }
@ -48,6 +48,7 @@ export class MonitorModel implements FrontendApplicationContribution {
toggleAutoscroll(): void { toggleAutoscroll(): void {
this._autoscroll = !this._autoscroll; this._autoscroll = !this._autoscroll;
this.storeState(); this.storeState();
this.storeState().then(() => this.onChangeEmitter.fire({ property: 'autoscroll', value: this._autoscroll }));
} }
get timestamp(): boolean { get timestamp(): boolean {
@ -56,7 +57,7 @@ export class MonitorModel implements FrontendApplicationContribution {
toggleTimestamp(): void { toggleTimestamp(): void {
this._timestamp = !this._timestamp; this._timestamp = !this._timestamp;
this.storeState(); this.storeState().then(() => this.onChangeEmitter.fire({ property: 'timestamp', value: this._timestamp }));
} }
get baudRate(): MonitorConfig.BaudRate { get baudRate(): MonitorConfig.BaudRate {
@ -65,7 +66,7 @@ export class MonitorModel implements FrontendApplicationContribution {
set baudRate(baudRate: MonitorConfig.BaudRate) { set baudRate(baudRate: MonitorConfig.BaudRate) {
this._baudRate = baudRate; this._baudRate = baudRate;
this.storeState().then(() => this.onChangeEmitter.fire(undefined)); this.storeState().then(() => this.onChangeEmitter.fire({ property: 'baudRate', value: this._baudRate }));
} }
get lineEnding(): MonitorModel.EOL { get lineEnding(): MonitorModel.EOL {
@ -74,7 +75,7 @@ export class MonitorModel implements FrontendApplicationContribution {
set lineEnding(lineEnding: MonitorModel.EOL) { set lineEnding(lineEnding: MonitorModel.EOL) {
this._lineEnding = lineEnding; this._lineEnding = lineEnding;
this.storeState(); this.storeState().then(() => this.onChangeEmitter.fire({ property: 'lineEnding', value: this._lineEnding }));
} }
protected restoreState(state: MonitorModel.State) { protected restoreState(state: MonitorModel.State) {
@ -82,7 +83,6 @@ export class MonitorModel implements FrontendApplicationContribution {
this._timestamp = state.timestamp; this._timestamp = state.timestamp;
this._baudRate = state.baudRate; this._baudRate = state.baudRate;
this._lineEnding = state.lineEnding; this._lineEnding = state.lineEnding;
this.onChangeEmitter.fire(undefined);
} }
protected async storeState(): Promise<void> { protected async storeState(): Promise<void> {
@ -104,6 +104,12 @@ export namespace MonitorModel {
baudRate: MonitorConfig.BaudRate; baudRate: MonitorConfig.BaudRate;
lineEnding: EOL; lineEnding: EOL;
} }
export namespace State {
export interface Change<K extends keyof State> {
readonly property: K;
readonly value: State[K];
}
}
export type EOL = '' | '\n' | '\r' | '\r\n'; export type EOL = '' | '\n' | '\r' | '\r\n';
export namespace EOL { export namespace EOL {

View File

@ -59,13 +59,13 @@ export class MonitorViewContribution extends AbstractViewContribution<MonitorWid
id: 'monitor-autoscroll', id: 'monitor-autoscroll',
render: () => this.renderAutoScrollButton(), render: () => this.renderAutoScrollButton(),
isVisible: widget => widget instanceof MonitorWidget, isVisible: widget => widget instanceof MonitorWidget,
onDidChange: this.model.onChange onDidChange: this.model.onChange as any // TODO: https://github.com/eclipse-theia/theia/pull/6696/
}); });
registry.registerItem({ registry.registerItem({
id: 'monitor-timestamp', id: 'monitor-timestamp',
render: () => this.renderTimestampButton(), render: () => this.renderTimestampButton(),
isVisible: widget => widget instanceof MonitorWidget, isVisible: widget => widget instanceof MonitorWidget,
onDidChange: this.model.onChange onDidChange: this.model.onChange as any // TODO: https://github.com/eclipse-theia/theia/pull/6696/
}); });
registry.registerItem({ registry.registerItem({
id: SerialMonitor.Commands.CLEAR_OUTPUT.id, id: SerialMonitor.Commands.CLEAR_OUTPUT.id,

View File

@ -88,7 +88,7 @@ export class MonitorWidget extends ReactWidget {
value: '' value: ''
}, },
{ {
label: 'Newline', label: 'New Line',
value: '\n' value: '\n'
}, },
{ {
@ -99,7 +99,7 @@ export class MonitorWidget extends ReactWidget {
label: 'Both NL & CR', label: 'Both NL & CR',
value: '\r\n' value: '\r\n'
} }
] ];
} }
protected get baudRates(): OptionsType<SelectOption<MonitorConfig.BaudRate>> { protected get baudRates(): OptionsType<SelectOption<MonitorConfig.BaudRate>> {
@ -111,28 +111,33 @@ export class MonitorWidget extends ReactWidget {
const { baudRates, lineEndings } = this; const { baudRates, lineEndings } = this;
const lineEnding = lineEndings.find(item => item.value === this.monitorModel.lineEnding) || lineEndings[1]; // Defaults to `\n`. const lineEnding = lineEndings.find(item => item.value === this.monitorModel.lineEnding) || lineEndings[1]; // Defaults to `\n`.
const baudRate = baudRates.find(item => item.value === this.monitorModel.baudRate) || baudRates[4]; // Defaults to `9600`. const baudRate = baudRates.find(item => item.value === this.monitorModel.baudRate) || baudRates[4]; // Defaults to `9600`.
return <div className='serial-monitor-container'> return <div className='serial-monitor'>
<div className='head'> <div className='head'>
<div className='send'> <div className='send'>
<SerialMonitorSendField <SerialMonitorSendInput
monitorConfig={this.monitorConnection.monitorConfig} monitorConfig={this.monitorConnection.monitorConfig}
resolveFocus={this.onFocusResolved} resolveFocus={this.onFocusResolved}
onSend={this.onSend} /> onSend={this.onSend} />
</div> </div>
<div className='config'> <div className='config'>
<ArduinoSelect <div className='select'>
maxMenuHeight={this.widgetHeight - 40} <ArduinoSelect
options={lineEndings} maxMenuHeight={this.widgetHeight - 40}
defaultValue={lineEnding} options={lineEndings}
onChange={this.onChangeLineEnding} />, defaultValue={lineEnding}
<ArduinoSelect onChange={this.onChangeLineEnding} />
maxMenuHeight={this.widgetHeight - 40} </div>
options={baudRates} <div className='select'>
defaultValue={baudRate} <ArduinoSelect
onChange={this.onChangeBaudRate} /> className='select'
maxMenuHeight={this.widgetHeight - 40}
options={baudRates}
defaultValue={baudRate}
onChange={this.onChangeBaudRate} />
</div>
</div> </div>
</div> </div>
<div id='serial-monitor-output-container'> <div className='body'>
<SerialMonitorOutput <SerialMonitorOutput
monitorModel={this.monitorModel} monitorModel={this.monitorModel}
monitorServiceClient={this.monitorServiceClient} monitorServiceClient={this.monitorServiceClient}
@ -157,7 +162,7 @@ export class MonitorWidget extends ReactWidget {
} }
export namespace SerialMonitorSendField { export namespace SerialMonitorSendInput {
export interface Props { export interface Props {
readonly monitorConfig?: MonitorConfig; readonly monitorConfig?: MonitorConfig;
readonly onSend: (text: string) => void; readonly onSend: (text: string) => void;
@ -168,9 +173,9 @@ export namespace SerialMonitorSendField {
} }
} }
export class SerialMonitorSendField extends React.Component<SerialMonitorSendField.Props, SerialMonitorSendField.State> { export class SerialMonitorSendInput extends React.Component<SerialMonitorSendInput.Props, SerialMonitorSendInput.State> {
constructor(props: SerialMonitorSendField.Props) { constructor(props: Readonly<SerialMonitorSendInput.Props>) {
super(props); super(props);
this.state = { value: '' }; this.state = { value: '' };
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
@ -235,6 +240,7 @@ export namespace SerialMonitorOutput {
} }
export interface State { export interface State {
content: string; content: string;
timestamp: boolean;
} }
} }
@ -248,7 +254,7 @@ export class SerialMonitorOutput extends React.Component<SerialMonitorOutput.Pro
constructor(props: Readonly<SerialMonitorOutput.Props>) { constructor(props: Readonly<SerialMonitorOutput.Props>) {
super(props); super(props);
this.state = { content: '' }; this.state = { content: '', timestamp: this.props.monitorModel.timestamp };
} }
render(): React.ReactNode { render(): React.ReactNode {
@ -270,11 +276,17 @@ export class SerialMonitorOutput extends React.Component<SerialMonitorOutput.Pro
if (eolIndex !== -1) { if (eolIndex !== -1) {
const line = chunk.substring(0, eolIndex + 1); const line = chunk.substring(0, eolIndex + 1);
chunk = chunk.slice(eolIndex + 1); chunk = chunk.slice(eolIndex + 1);
const content = `${this.state.content}${false ? `${dateFormat(new Date(), 'H:M:ss.l')} -> ` : ''}${line}`; const content = `${this.state.content}${this.state.timestamp ? `${dateFormat(new Date(), 'H:M:ss.l')} -> ` : ''}${line}`;
this.setState({ content }); this.setState({ content });
} }
}), }),
this.props.clearConsoleEvent(() => this.setState({ content: '' })) this.props.clearConsoleEvent(() => this.setState({ content: '' })),
this.props.monitorModel.onChange(({ property }) => {
if (property === 'timestamp') {
const { timestamp } = this.props.monitorModel;
this.setState({ timestamp });
}
})
]); ]);
} }
@ -283,7 +295,8 @@ export class SerialMonitorOutput extends React.Component<SerialMonitorOutput.Pro
} }
componentWillUnmount(): void { componentWillUnmount(): void {
this.toDisposeBeforeUnmount.dispose() // TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
this.toDisposeBeforeUnmount.dispose();
} }
protected scrollToBottom(): void { protected scrollToBottom(): void {

View File

@ -5,51 +5,49 @@
background-position-x: 19px; background-position-x: 19px;
} }
.serial-monitor-container { .serial-monitor {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.serial-monitor-container .head { .serial-monitor .head {
display: flex; display: flex;
padding: 5px; padding: 5px;
background: var(--theia-layout-color0); background: var(--theia-layout-color0);
height: 27px; height: 27px;
} }
.serial-monitor-container .head .send { .serial-monitor .head .send {
display: flex; display: flex;
flex: 1; flex: 1;
margin-right: 4px;
} }
.serial-monitor-container .head .send > input { .serial-monitor .head .send > input {
line-height: var(--theia-content-line-height); line-height: var(--theia-content-line-height);
width: 100%; width: 100%;
} }
.serial-monitor-container .head .send > input:focus { .serial-monitor .head .send > input:focus {
border-color: var(--theia-accent-color3); border-color: var(--theia-accent-color3);
} }
.serial-monitor-container .head .send > input.not-connected { .serial-monitor .head .send > input.not-connected {
background-color: var(--theia-warn-color0); background-color: var(--theia-warn-color0);
} }
.serial-monitor-container .head .send input#serial-monitor-send { .serial-monitor .head .config {
color: var(--theia-ui-font-color1);
flex: 1;
}
.serial-monitor-container .head .config {
display: flex; display: flex;
} }
#serial-monitor-output-container { .serial-monitor .head .config .select {
margin-left: 5px;
}
.serial-monitor .body {
overflow: auto; overflow: auto;
flex: 1; flex: 1;
padding: 6px; padding: 5px;
} }
.p-TabBar-toolbar .item.arduino-monitor { .p-TabBar-toolbar .item.arduino-monitor {