mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-09 02:18:32 +00:00
Implemented the Widget
Re-introduced bottom panel tabs Signed-off-by: jbicker <jan.bicker@typefox.io>
This commit is contained in:
@@ -47,6 +47,7 @@ import { BoardsToolBarItem } from './boards/boards-toolbar-item';
|
||||
import { BoardsConfig } from './boards/boards-config';
|
||||
import { MonitorService } from '../common/protocol/monitor-service';
|
||||
import { ConfigService } from '../common/protocol/config-service';
|
||||
import { MonitorConnection } from './monitor/monitor-connection';
|
||||
|
||||
export namespace ArduinoMenus {
|
||||
export const SKETCH = [...MAIN_MENU_BAR, '3_sketch'];
|
||||
@@ -143,6 +144,8 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
|
||||
protected boardsToolbarItem: BoardsToolBarItem | null;
|
||||
protected wsSketchCount: number = 0;
|
||||
@@ -244,6 +247,9 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
return;
|
||||
}
|
||||
|
||||
const connectionConfig = this.monitorConnection.connectionConfig;
|
||||
await this.monitorConnection.disconnect();
|
||||
|
||||
try {
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
if (!boardsConfig || !boardsConfig.selectedBoard) {
|
||||
@@ -256,6 +262,10 @@ export class ArduinoFrontendContribution implements TabBarToolbarContribution, C
|
||||
await this.coreService.upload({ uri: uri.toString(), board: boardsConfig.selectedBoard, port: selectedPort });
|
||||
} catch (e) {
|
||||
await this.messageService.error(e.toString());
|
||||
} finally {
|
||||
if (connectionConfig) {
|
||||
await this.monitorConnection.connect(connectionConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -57,12 +57,12 @@ import { BoardItemRenderer } from './boards/boards-item-renderer';
|
||||
import { MonitorServiceClientImpl } from './monitor/monitor-service-client-impl';
|
||||
import { MonitorServicePath, MonitorService, MonitorServiceClient } from '../common/protocol/monitor-service';
|
||||
import { ConfigService, ConfigServicePath } from '../common/protocol/config-service';
|
||||
import { MonitorWidget } from './monitor/monitor-widget';
|
||||
import { MonitorViewContribution } from './monitor/monitor-view-contribution';
|
||||
import { MonitorConnection } from './monitor/monitor-connection';
|
||||
import { MonitorModel } from './monitor/monitor-model';
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
if (!ARDUINO_PRO_MODE) {
|
||||
require('../../src/browser/style/silent-bottom-panel-tabs.css');
|
||||
}
|
||||
|
||||
export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => {
|
||||
ElementQueries.listen();
|
||||
ElementQueries.init();
|
||||
@@ -155,12 +155,23 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un
|
||||
return workspaceServiceExt;
|
||||
});
|
||||
|
||||
// Serial Monitor
|
||||
bind(MonitorModel).toSelf().inSingletonScope();
|
||||
bind(MonitorWidget).toSelf();
|
||||
bindViewContribution(bind, MonitorViewContribution);
|
||||
bind(TabBarToolbarContribution).toService(MonitorViewContribution);
|
||||
bind(WidgetFactory).toDynamicValue(context => ({
|
||||
id: MonitorWidget.ID,
|
||||
createWidget: () => context.container.get(MonitorWidget)
|
||||
}));
|
||||
// Frontend binding for the monitor service.
|
||||
bind(MonitorService).toDynamicValue(context => {
|
||||
const connection = context.container.get(WebSocketConnectionProvider);
|
||||
const client = context.container.get(MonitorServiceClientImpl);
|
||||
return connection.createProxy(MonitorServicePath, client);
|
||||
}).inSingletonScope();
|
||||
// MonitorConnection
|
||||
bind(MonitorConnection).toSelf().inSingletonScope();
|
||||
// Monitor service client to receive and delegate notifications from the backend.
|
||||
bind(MonitorServiceClientImpl).toSelf().inSingletonScope();
|
||||
bind(MonitorServiceClient).toDynamicValue(context => {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { injectable, inject } from "inversify";
|
||||
import { MonitorService, ConnectionConfig } from "../../common/protocol/monitor-service";
|
||||
import { Emitter, Event } from "@theia/core";
|
||||
|
||||
@injectable()
|
||||
export class MonitorConnection {
|
||||
|
||||
@inject(MonitorService)
|
||||
protected readonly monitorService: MonitorService;
|
||||
|
||||
protected _connectionId: string | undefined;
|
||||
|
||||
protected _connectionConfig: ConnectionConfig;
|
||||
|
||||
protected readonly onConnectionChangedEmitter = new Emitter<string | undefined>();
|
||||
readonly onConnectionChanged: Event<string | undefined> = this.onConnectionChangedEmitter.event;
|
||||
|
||||
get connectionId(): string | undefined {
|
||||
return this._connectionId;
|
||||
}
|
||||
|
||||
set connectionId(cid: string | undefined) {
|
||||
this._connectionId = cid;
|
||||
}
|
||||
|
||||
get connectionConfig(): ConnectionConfig {
|
||||
return this._connectionConfig;
|
||||
}
|
||||
|
||||
async connect(config: ConnectionConfig): Promise<string | undefined> {
|
||||
if (this._connectionId) {
|
||||
await this.disconnect();
|
||||
}
|
||||
const { connectionId } = await this.monitorService.connect(config);
|
||||
this._connectionId = connectionId;
|
||||
this._connectionConfig = config;
|
||||
|
||||
this.onConnectionChangedEmitter.fire(this._connectionId);
|
||||
|
||||
return connectionId;
|
||||
}
|
||||
|
||||
async disconnect(): Promise<boolean> {
|
||||
let result = true;
|
||||
const connections = await this.monitorService.getConnectionIds();
|
||||
if (this._connectionId && connections.findIndex(id => id === this._connectionId) >= 0) {
|
||||
console.log('>>> Disposing existing monitor connection before establishing a new one...');
|
||||
result = await this.monitorService.disconnect(this._connectionId);
|
||||
if (!result) {
|
||||
// TODO: better!!!
|
||||
console.error(`Could not close connection: ${this._connectionId}. Check the backend logs.`);
|
||||
} else {
|
||||
console.log(`<<< Disposed ${this._connectionId} connection.`);
|
||||
this._connectionId = undefined;
|
||||
}
|
||||
this.onConnectionChangedEmitter.fire(this._connectionId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
31
arduino-ide-extension/src/browser/monitor/monitor-model.ts
Normal file
31
arduino-ide-extension/src/browser/monitor/monitor-model.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { injectable } from "inversify";
|
||||
import { Emitter } from "@theia/core";
|
||||
|
||||
@injectable()
|
||||
export class MonitorModel {
|
||||
|
||||
protected readonly onChangeEmitter = new Emitter<void>();
|
||||
|
||||
readonly onChange = this.onChangeEmitter.event;
|
||||
|
||||
protected _autoscroll: boolean = true;
|
||||
protected _timestamp: boolean = false;
|
||||
|
||||
get autoscroll(): boolean {
|
||||
return this._autoscroll;
|
||||
}
|
||||
|
||||
get timestamp(): boolean {
|
||||
return this._timestamp;
|
||||
}
|
||||
|
||||
toggleAutoscroll(): void {
|
||||
this._autoscroll = !this._autoscroll;
|
||||
this.onChangeEmitter.fire(undefined);
|
||||
}
|
||||
|
||||
toggleTimestamp(): void {
|
||||
this._timestamp = !this._timestamp;
|
||||
this.onChangeEmitter.fire(undefined);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import * as React from 'react';
|
||||
import { injectable, inject } from "inversify";
|
||||
import { AbstractViewContribution } from "@theia/core/lib/browser";
|
||||
import { MonitorWidget } from "./monitor-widget";
|
||||
import { MenuModelRegistry, Command, CommandRegistry } from "@theia/core";
|
||||
import { ArduinoMenus } from "../arduino-frontend-contribution";
|
||||
import { TabBarToolbarContribution, TabBarToolbarRegistry } from "@theia/core/lib/browser/shell/tab-bar-toolbar";
|
||||
import { MonitorModel } from './monitor-model';
|
||||
|
||||
export namespace SerialMonitor {
|
||||
export namespace Commands {
|
||||
export const AUTOSCROLL: Command = {
|
||||
id: 'serial-monitor-autoscroll',
|
||||
label: 'Autoscroll'
|
||||
}
|
||||
export const TIMESTAMP: Command = {
|
||||
id: 'serial-monitor-timestamp',
|
||||
label: 'Timestamp'
|
||||
}
|
||||
export const CLEAR_OUTPUT: Command = {
|
||||
id: 'serial-monitor-clear-output',
|
||||
label: 'Clear Output',
|
||||
iconClass: 'clear-all'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class MonitorViewContribution extends AbstractViewContribution<MonitorWidget> implements TabBarToolbarContribution {
|
||||
|
||||
static readonly OPEN_SERIAL_MONITOR = MonitorWidget.ID + ':toggle';
|
||||
|
||||
@inject(MonitorModel) protected readonly model: MonitorModel;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: MonitorWidget.ID,
|
||||
widgetName: 'Serial Monitor',
|
||||
defaultWidgetOptions: {
|
||||
area: 'bottom'
|
||||
},
|
||||
toggleCommandId: MonitorViewContribution.OPEN_SERIAL_MONITOR,
|
||||
toggleKeybinding: 'ctrl+shift+m'
|
||||
})
|
||||
}
|
||||
|
||||
registerMenus(menus: MenuModelRegistry): void {
|
||||
if (this.toggleCommand) {
|
||||
menus.registerMenuAction(ArduinoMenus.TOOLS, {
|
||||
commandId: this.toggleCommand.id,
|
||||
label: 'Serial Monitor'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async registerToolbarItems(registry: TabBarToolbarRegistry) {
|
||||
registry.registerItem({
|
||||
id: 'monitor-autoscroll',
|
||||
tooltip: 'Toggle Autoscroll',
|
||||
render: () => this.renderAutoScrollButton(),
|
||||
isVisible: widget => widget instanceof MonitorWidget,
|
||||
onDidChange: this.model.onChange
|
||||
});
|
||||
registry.registerItem({
|
||||
id: 'monitor-timestamp',
|
||||
tooltip: 'Toggle Timestamp',
|
||||
render: () => this.renderTimestampButton(),
|
||||
isVisible: widget => widget instanceof MonitorWidget,
|
||||
onDidChange: this.model.onChange
|
||||
});
|
||||
registry.registerItem({
|
||||
id: SerialMonitor.Commands.CLEAR_OUTPUT.id,
|
||||
command: SerialMonitor.Commands.CLEAR_OUTPUT.id,
|
||||
tooltip: 'Clear Output'
|
||||
});
|
||||
}
|
||||
|
||||
registerCommands(commands: CommandRegistry): void {
|
||||
super.registerCommands(commands);
|
||||
commands.registerCommand(SerialMonitor.Commands.CLEAR_OUTPUT, {
|
||||
isEnabled: widget => widget instanceof MonitorWidget,
|
||||
isVisible: widget => widget instanceof MonitorWidget,
|
||||
execute: widget => {
|
||||
if (widget instanceof MonitorWidget) {
|
||||
widget.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected renderAutoScrollButton(): React.ReactNode {
|
||||
|
||||
return <React.Fragment>
|
||||
<div
|
||||
className={`item enabled fa fa-angle-double-down arduino-monitor ${this.model.autoscroll ? 'toggled' : ''}`}
|
||||
onClick={this.toggleAutoScroll}
|
||||
></div>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
protected readonly toggleAutoScroll = () => this.doToggleAutoScroll();
|
||||
protected async doToggleAutoScroll() {
|
||||
this.model.toggleAutoscroll();
|
||||
}
|
||||
|
||||
protected renderTimestampButton(): React.ReactNode {
|
||||
|
||||
return <React.Fragment>
|
||||
<div
|
||||
className={`item enabled fa fa-clock-o arduino-monitor ${this.model.timestamp ? 'toggled' : ''}`}
|
||||
onClick={this.toggleTimestamp}
|
||||
></div>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
protected readonly toggleTimestamp = () => this.doToggleTimestamp();
|
||||
protected async doToggleTimestamp() {
|
||||
this.model.toggleTimestamp();
|
||||
}
|
||||
}
|
||||
346
arduino-ide-extension/src/browser/monitor/monitor-widget.tsx
Normal file
346
arduino-ide-extension/src/browser/monitor/monitor-widget.tsx
Normal file
@@ -0,0 +1,346 @@
|
||||
import { ReactWidget, Message } from "@theia/core/lib/browser";
|
||||
import { postConstruct, injectable, inject } from "inversify";
|
||||
import * as React from 'react';
|
||||
import Select, { components } from 'react-select';
|
||||
import { Styles } from "react-select/src/styles";
|
||||
import { ThemeConfig } from "react-select/src/theme";
|
||||
import { OptionsType } from "react-select/src/types";
|
||||
import { MonitorServiceClientImpl } from "./monitor-service-client-impl";
|
||||
import { MessageService } from "@theia/core";
|
||||
import { ConnectionConfig, MonitorService } from "../../common/protocol/monitor-service";
|
||||
import { MonitorConnection } from "./monitor-connection";
|
||||
import { BoardsServiceClientImpl } from "../boards/boards-service-client-impl";
|
||||
import { AttachedSerialBoard, BoardsService, Board } from "../../common/protocol/boards-service";
|
||||
import { BoardsConfig } from "../boards/boards-config";
|
||||
import { MonitorModel } from "./monitor-model";
|
||||
|
||||
export namespace SerialMonitorSendField {
|
||||
export interface Props {
|
||||
onSend: (text: string) => void
|
||||
}
|
||||
|
||||
export interface State {
|
||||
value: string;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorSendField extends React.Component<SerialMonitorSendField.Props, SerialMonitorSendField.State> {
|
||||
constructor(props: SerialMonitorSendField.Props) {
|
||||
super(props);
|
||||
this.state = { value: '' };
|
||||
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <React.Fragment>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input type='text' id='serial-monitor-send' autoComplete='off' value={this.state.value} onChange={this.handleChange} />
|
||||
<input className="btn" type="submit" value="Submit" />
|
||||
</form>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
protected handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
this.setState({ value: event.target.value });
|
||||
}
|
||||
|
||||
protected handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
this.props.onSend(this.state.value);
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SerialMonitorOutput {
|
||||
export interface Props {
|
||||
lines: string[];
|
||||
model: MonitorModel;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorOutput extends React.Component<SerialMonitorOutput.Props> {
|
||||
protected theEnd: HTMLDivElement | null;
|
||||
|
||||
render() {
|
||||
let result = '';
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
whiteSpace: 'pre',
|
||||
fontFamily: 'monospace',
|
||||
};
|
||||
|
||||
for (let text of this.props.lines) {
|
||||
result += text;
|
||||
}
|
||||
if (result.length === 0) {
|
||||
result = '';
|
||||
}
|
||||
return <React.Fragment>
|
||||
<div style={style}>{result}</div>
|
||||
<div style={{ float: "left", clear: "both" }}
|
||||
ref={(el) => { this.theEnd = el; }}>
|
||||
</div>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
protected scrollToBottom() {
|
||||
if (this.theEnd) {
|
||||
this.theEnd.scrollIntoView();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.model.autoscroll) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.model.autoscroll) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SelectOption {
|
||||
label: string;
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class MonitorWidget extends ReactWidget {
|
||||
|
||||
static readonly ID = 'serial-monitor';
|
||||
|
||||
protected lines: string[];
|
||||
protected tempData: string;
|
||||
protected _baudRate: number;
|
||||
protected _lineEnding: string;
|
||||
|
||||
constructor(
|
||||
@inject(MonitorServiceClientImpl) protected readonly serviceClient: MonitorServiceClientImpl,
|
||||
@inject(MonitorConnection) protected readonly connection: MonitorConnection,
|
||||
@inject(MonitorService) protected readonly monitorService: MonitorService,
|
||||
@inject(BoardsServiceClientImpl) protected readonly boardsServiceClient: BoardsServiceClientImpl,
|
||||
@inject(MessageService) protected readonly messageService: MessageService,
|
||||
@inject(BoardsService) protected readonly boardsService: BoardsService,
|
||||
@inject(MonitorModel) protected readonly model: MonitorModel
|
||||
) {
|
||||
super();
|
||||
|
||||
this.id = MonitorWidget.ID;
|
||||
this.title.label = 'Serial Monitor';
|
||||
|
||||
this.lines = [];
|
||||
this.tempData = '';
|
||||
this._lineEnding = '\n';
|
||||
|
||||
this.toDisposeOnDetach.push(serviceClient.onRead(({ data, connectionId }) => {
|
||||
this.tempData += data;
|
||||
if (this.tempData.endsWith('\n')) {
|
||||
if (this.model.timestamp) {
|
||||
const nu = new Date();
|
||||
this.tempData = `${nu.getHours()}:${nu.getMinutes()}:${nu.getSeconds()}.${nu.getMilliseconds()} -> ` + this.tempData;
|
||||
}
|
||||
this.lines.push(this.tempData);
|
||||
this.tempData = '';
|
||||
this.update();
|
||||
}
|
||||
}));
|
||||
|
||||
// TODO onError
|
||||
}
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.update();
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.lines = [];
|
||||
this.update();
|
||||
}
|
||||
|
||||
get baudRate(): number | undefined {
|
||||
return this._baudRate;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message) {
|
||||
super.onAfterAttach(msg);
|
||||
this.clear();
|
||||
this.connect();
|
||||
this.toDisposeOnDetach.push(this.boardsServiceClient.onBoardsChanged(async states => {
|
||||
const currentConnectionConfig = this.connection.connectionConfig;
|
||||
const connectedBoard = states.newState.boards
|
||||
.filter(AttachedSerialBoard.is)
|
||||
.find(board => {
|
||||
const potentiallyConnected = currentConnectionConfig && currentConnectionConfig.board;
|
||||
if (AttachedSerialBoard.is(potentiallyConnected)) {
|
||||
return Board.equals(board, potentiallyConnected) && board.port === potentiallyConnected.port;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (!connectedBoard) {
|
||||
this.close();
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDisposeOnDetach.push(this.connection.onConnectionChanged(() => {
|
||||
this.clear();
|
||||
}));
|
||||
}
|
||||
|
||||
protected onBeforeDetach(msg: Message) {
|
||||
super.onBeforeDetach(msg);
|
||||
this.connection.disconnect();
|
||||
}
|
||||
|
||||
protected async connect() {
|
||||
const config = await this.getConnectionConfig();
|
||||
if (config) {
|
||||
this.connection.connect(config);
|
||||
}
|
||||
}
|
||||
|
||||
protected async getConnectionConfig(): Promise<ConnectionConfig | undefined> {
|
||||
const baudRate = this.baudRate;
|
||||
const { boardsConfig } = this.boardsServiceClient;
|
||||
const { selectedBoard, selectedPort } = boardsConfig;
|
||||
if (!selectedBoard) {
|
||||
this.messageService.warn('No boards selected.');
|
||||
return;
|
||||
}
|
||||
const { name } = selectedBoard;
|
||||
if (!selectedPort) {
|
||||
this.messageService.warn(`No ports selected for board: '${name}'.`);
|
||||
return;
|
||||
}
|
||||
const attachedBoards = await this.boardsService.getAttachedBoards();
|
||||
const connectedBoard = attachedBoards.boards.filter(AttachedSerialBoard.is).find(board => BoardsConfig.Config.sameAs(boardsConfig, board));
|
||||
if (!connectedBoard) {
|
||||
this.messageService.warn(`The selected '${name}' board is not connected on ${selectedPort}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
baudRate,
|
||||
board: selectedBoard,
|
||||
port: selectedPort
|
||||
}
|
||||
}
|
||||
|
||||
protected getLineEndings(): OptionsType<SelectOption> {
|
||||
return [
|
||||
{
|
||||
label: 'No Line Ending',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: 'Newline',
|
||||
value: '\n'
|
||||
},
|
||||
{
|
||||
label: 'Carriage Return',
|
||||
value: '\r'
|
||||
},
|
||||
{
|
||||
label: 'Both NL & CR',
|
||||
value: '\r\n'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
protected getBaudRates(): OptionsType<SelectOption> {
|
||||
const baudRates = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200];
|
||||
return baudRates.map<SelectOption>(baudRate => ({ label: baudRate + ' baud', value: baudRate }))
|
||||
}
|
||||
|
||||
protected render(): React.ReactNode {
|
||||
const le = this.getLineEndings();
|
||||
const br = this.getBaudRates();
|
||||
return <React.Fragment>
|
||||
<div className='serial-monitor-container'>
|
||||
<div className='head'>
|
||||
<div className='send'>
|
||||
<SerialMonitorSendField onSend={this.onSend} />
|
||||
</div>
|
||||
<div className='config'>
|
||||
{this.renderSelectField('arduino-serial-monitor-line-endings', le, le[1], this.onChangeLineEnding)}
|
||||
{this.renderSelectField('arduino-serial-monitor-baud-rates', br, br[4], this.onChangeBaudRate)}
|
||||
</div>
|
||||
</div>
|
||||
<div id='serial-monitor-output-container'>
|
||||
<SerialMonitorOutput model={this.model} lines={this.lines} />
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
protected readonly onSend = (value: string) => this.doSend(value);
|
||||
protected async doSend(value: string) {
|
||||
const { connectionId } = this.connection;
|
||||
if (connectionId) {
|
||||
this.monitorService.send(connectionId, value + this._lineEnding);
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly onChangeLineEnding = (le: SelectOption) => {
|
||||
this._lineEnding = typeof le.value === 'string' ? le.value : '\n';
|
||||
}
|
||||
|
||||
protected readonly onChangeBaudRate = (br: SelectOption) => {
|
||||
this._baudRate = typeof br.value === 'number' ? br.value : 9600;
|
||||
}
|
||||
|
||||
protected renderSelectField(id: string, options: OptionsType<SelectOption>, defaultVal: SelectOption, onChange: (v: SelectOption) => void): React.ReactNode {
|
||||
const height = 25;
|
||||
const selectStyles: Styles = {
|
||||
control: (provided, state) => ({
|
||||
...provided,
|
||||
width: 200
|
||||
}),
|
||||
dropdownIndicator: (p, s) => ({
|
||||
...p,
|
||||
padding: 0
|
||||
}),
|
||||
indicatorSeparator: (p, s) => ({
|
||||
display: 'none'
|
||||
}),
|
||||
indicatorsContainer: (p, s) => ({
|
||||
padding: '0 5px'
|
||||
}),
|
||||
menu: (p, s) => ({
|
||||
...p,
|
||||
marginTop: 0
|
||||
})
|
||||
};
|
||||
const theme: ThemeConfig = theme => ({
|
||||
...theme,
|
||||
borderRadius: 0,
|
||||
spacing: {
|
||||
controlHeight: height,
|
||||
baseUnit: 2,
|
||||
menuGutter: 4
|
||||
}
|
||||
});
|
||||
const DropdownIndicator = (
|
||||
props: React.Props<typeof components.DropdownIndicator>
|
||||
) => {
|
||||
return (
|
||||
<span className='fa fa-caret-down caret'></span>
|
||||
);
|
||||
};
|
||||
return <Select
|
||||
options={options}
|
||||
defaultValue={defaultVal}
|
||||
onChange={onChange}
|
||||
components={{ DropdownIndicator }}
|
||||
theme={theme}
|
||||
styles={selectStyles}
|
||||
classNamePrefix='sms'
|
||||
className='serial-monitor-select'
|
||||
id={id}
|
||||
/>
|
||||
}
|
||||
}
|
||||
@@ -196,6 +196,7 @@ button.theia-button.main {
|
||||
background: #f7f7f7;
|
||||
border: 3px solid var(--theia-border-color2);
|
||||
margin: -3px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.arduino-boards-dropdown-item {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import './list-widget.css';
|
||||
@import './board-select-dialog.css';
|
||||
@import './main.css';
|
||||
@import './editor.css';
|
||||
@import './editor.css';
|
||||
@import './serial-monitor.css';
|
||||
86
arduino-ide-extension/src/browser/style/serial-monitor.css
Normal file
86
arduino-ide-extension/src/browser/style/serial-monitor.css
Normal file
@@ -0,0 +1,86 @@
|
||||
.serial-monitor-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
background: var(--theia-brand-color2);
|
||||
height: 27px;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send {
|
||||
display: flex;
|
||||
flex:1;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send .btn {
|
||||
display: flex;
|
||||
padding: 0 5px;
|
||||
align-items: center;
|
||||
background: var(--theia-brand-color3);
|
||||
color: var(--theia-ui-dialog-font-color);
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send form {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send input#serial-monitor-send {
|
||||
background: var(--theia-layout-color0);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .send input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .config {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.serial-monitor-container .head .config .serial-monitor-select {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#serial-monitor-output-container {
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item.arduino-monitor {
|
||||
width: 24px;
|
||||
justify-content: center;
|
||||
font-size: medium;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item.arduino-monitor.toggled {
|
||||
border: var(--theia-ui-button-color-hover) var(--theia-border-width) solid;
|
||||
}
|
||||
|
||||
.p-TabBar-toolbar .item .clear-all {
|
||||
background: var(--theia-icon-clear) no-repeat;
|
||||
}
|
||||
|
||||
/* React Select Styles */
|
||||
.serial-monitor-select .sms__control {
|
||||
border: var(--theia-border-color1) var(--theia-border-width) solid;
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__option--is-selected {
|
||||
background-color: var(--theia-ui-button-color-secondary-hover);
|
||||
color: var(--theia-content-font-color0);
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__option--is-focused {
|
||||
background-color: var(--theia-ui-button-color-secondary-hover);
|
||||
}
|
||||
|
||||
.serial-monitor-select .sms__menu {
|
||||
background-color: var(--theia-layout-color1);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.p-Widget.p-TabBar.theia-app-centers.theia-app-bottom .p-TabBar-content-container.ps {
|
||||
display: none;
|
||||
}
|
||||
Reference in New Issue
Block a user