Skip to content

Commit

Permalink
Added ability for users to change tab names from menu items in trace …
Browse files Browse the repository at this point in the history
…explorer widget as outlined in issue eclipse-cdt-cloud#384

Signed-off-by: Nikolai Peram <[email protected]>
  • Loading branch information
thefinaljob committed Aug 18, 2021
1 parent 820176f commit 33d7ea8
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 19 deletions.
4 changes: 4 additions & 0 deletions packages/base/src/signals/signal-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export declare interface SignalManager {
}

export const Signals = {
TABNAME_CHANGED: 'tab changed',
TRACE_OPENED: 'trace opened',
TRACE_CLOSED: 'trace closed',
EXPERIMENT_OPENED: 'experiment opened',
Expand All @@ -43,6 +44,9 @@ export const Signals = {
};

export class SignalManager extends EventEmitter implements SignalManager {
fireTabChangedSignal(tabName: string, expirementUUID: string): void {
this.emit(Signals.TABNAME_CHANGED, {tabName, expirementUUID});
}
fireTraceOpenedSignal(trace: Trace): void {
this.emit(Signals.TRACE_OPENED, trace);
}
Expand Down
147 changes: 147 additions & 0 deletions packages/react-components/src/trace-explorer/menu-item-trace.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import * as React from 'react';
import { Trace } from 'tsp-typescript-client';
import { signalManager, Signals } from '@trace-viewer/base/lib/signals/signal-manager';

interface MenuItemProps {
index: number;
experimentName: string;
experimentUUID: string;
traces: Trace[];
onTabNameChange: (tabEditingOpen: string, index: number) => void;
menuItemTraceContainerClassName: string;
handleClickEvent: (event: React.MouseEvent<HTMLDivElement>, experimentName: string) => void;
handleContextMenuEvent: (event: React.MouseEvent<HTMLDivElement>, experimentUUID: string) => void;
}
interface MenuItemState {
editingTab: boolean;
oldexpirementName: string;
expirementNameState: string;
}

export class MenuItemTrace extends React.Component<MenuItemProps, MenuItemState> {

private wrapper: React.RefObject<HTMLDivElement>;

constructor(menuItemProps: MenuItemProps) {
super(menuItemProps);
this.state = {
oldexpirementName: this.props.experimentName,
expirementNameState: this.props.experimentName,
editingTab: false
};
this.wrapper = React.createRef();
}

componentDidMount(): void {
document.addEventListener('click', this.handleClickOutside);
}

componentWillUnmount(): void {
document.removeEventListener('click', this.handleClickOutside);
}

submitNewTab(): void {
if (this.state.expirementNameState.length > 0) {
this.setState({
editingTab: false,
oldexpirementName: this.state.expirementNameState
});
this.props.onTabNameChange(this.state.expirementNameState, this.props.index);
} else {
this.setState({
editingTab: false,
expirementNameState: this.state.oldexpirementName
});
signalManager().fireTabChangedSignal(this.state.oldexpirementName, this.props.experimentUUID);
}
}

handleClickOutside = (event: Event): void => {
const node = this.wrapper.current;
if ((!node || !node.contains(event.target as Node)) && this.state.editingTab === true) {
this.submitNewTab();
}
};

protected handleEnterPress(event: React.KeyboardEvent<HTMLInputElement>): void{
if (event.key === 'Enter' && this.state.editingTab === true){
this.submitNewTab();
}
}

protected changeText(event: React.ChangeEvent<HTMLInputElement>): void{
const newName = event.target.value.toString();
this.setState({
expirementNameState : newName
});
signalManager().fireTabChangedSignal(newName, this.props.experimentUUID);
}
protected inputTab(): React.ReactNode {
return <input name="tab-name" className="theia-input"
defaultValue = {this.state.expirementNameState}
onChange = {e => (this.changeText(e))}
onClick = {e => e.stopPropagation()}
onKeyPress = {e => (this.handleEnterPress(e))}
maxLength = {50}
/>;
}
protected renderEditTraceName(event: React.MouseEvent<HTMLDivElement>): void {
this.setState(() => ({
editingTab: true
}));
event.stopPropagation();
}
protected renderTracesForExperiment = (): React.ReactNode => this.doRenderTracesForExperiment();
protected doRenderTracesForExperiment(): React.ReactNode {
const tracePaths = this.props.traces;
return (
<div className='trace-element-path-container'>
{tracePaths.map(trace => (
<div className='trace-element-path child-element' id={trace.UUID} key={trace.UUID}>
{` > ${trace.name}`}
</div>
))}
</div>
);
}
protected subscribeToExplorerEvents(): void {
signalManager().on(Signals.OUTPUT_ADDED, this.changeText);
}

render(): JSX.Element {
return (
<div className={this.props.menuItemTraceContainerClassName}
id={`${this.props.menuItemTraceContainerClassName}-${this.props.index}`}
onClick={event => { this.props.handleClickEvent(event, this.props.experimentUUID); }}
onContextMenu={event => { this.props.handleContextMenuEvent(event, this.props.experimentUUID); }}
data-id={`${this.props.index}`}
ref={this.wrapper}>
<div className='trace-element-container'>
<div className='trace-element-info' >
<h4 className='trace-element-name'>
{this.state.editingTab ? this.inputTab() : this.state.expirementNameState}
<div className='edit-trace-name' onClick={e => {this.renderEditTraceName(e);}}>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 512 512" enableBackground="new 0 0 512 512" xmlSpace="preserve">
<g>
<path fill="#020202" d="M422.953,176.019c0.549-0.48,1.09-0.975,1.612-1.498l21.772-21.772c12.883-12.883,12.883-33.771,0-46.654
l-40.434-40.434c-12.883-12.883-33.771-12.883-46.653,0l-21.772,21.772c-0.523,0.523-1.018,1.064-1.498,1.613L422.953,176.019z"/>
<polygon fill="#020202" points="114.317,397.684 157.317,440.684 106.658,448.342 56,456 63.658,405.341 71.316,354.683 "/>
<polygon fill="#020202" points="349.143,125.535 118.982,355.694 106.541,343.253 336.701,113.094 324.26,100.653 81.659,343.253
168.747,430.341 411.348,187.74 "/>
</g>
</svg>
</div>
</h4>
{ this.renderTracesForExperiment() }
</div>
{/* <div className='trace-element-options'>
<button className='share-context-button' onClick={this.handleShareButtonClick.bind(this, props.index)}>
<FontAwesomeIcon icon={faShareSquare} />
</button>
</div> */}
</div>
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCopy } from '@fortawesome/free-solid-svg-icons';
import { OpenedTracesUpdatedSignalPayload } from '@trace-viewer/base/lib/signals/opened-traces-updated-signal-payload';
import { ITspClientProvider } from '@trace-viewer/base/lib/tsp-client-provider';
import { MenuItemTrace } from './menu-item-trace';

export interface ReactOpenTracesWidgetProps {
id: string,
Expand Down Expand Up @@ -44,6 +45,7 @@ export class ReactOpenTracesWidget extends React.Component<ReactOpenTracesWidget
signalManager().on(Signals.EXPERIMENT_CLOSED, this._onExperimentClosed);
signalManager().on(Signals.TRACEVIEWERTAB_ACTIVATED, this._onOpenedTracesWidgetActivated);

this.handleTabNameUpdate = this.handleTabNameUpdate.bind(this);
this._experimentManager = this.props.tspClientProvider.getExperimentManager();
this.props.tspClientProvider.addTspClientChangeListener(() => {
this._experimentManager = this.props.tspClientProvider.getExperimentManager();
Expand Down Expand Up @@ -151,25 +153,16 @@ export class ReactOpenTracesWidget extends React.Component<ReactOpenTracesWidget
if (props.index === this.state.selectedExperimentIndex) {
traceContainerClassName = traceContainerClassName + ' theia-mod-selected';
}
return <div className={traceContainerClassName}
id={`${traceContainerClassName}-${props.index}`}
key={props.key}
style={props.style}
onClick={event => { this.handleClickEvent(event, traceUUID); }}
onContextMenu={event => { this.handleContextMenuEvent(event, traceUUID); }}
data-id={`${props.index}`}>
<div className='trace-element-container'>
<div className='trace-element-info' >
<h4 className='trace-element-name'>{traceName}</h4>
{this.renderTracesForExperiment(props.index)}
</div>
{/* <div className='trace-element-options'>
<button className='share-context-button' onClick={this.handleShareButtonClick.bind(this, props.index)}>
<FontAwesomeIcon icon={faShareSquare} />
</button>
</div> */}
</div>
</div>;
return <MenuItemTrace
onTabNameChange = {this.handleTabNameUpdate}
menuItemTraceContainerClassName = {traceContainerClassName}
traces={this.state.openedExperiments[props.index].traces}
experimentName={traceName}
experimentUUID={traceUUID}
index={props.index}
handleContextMenuEvent={this.handleContextMenuEvent}
handleClickEvent={this.handleClickEvent}
/>;
}

protected renderTracesForExperiment(index: number): React.ReactNode {
Expand Down Expand Up @@ -235,15 +228,33 @@ export class ReactOpenTracesWidget extends React.Component<ReactOpenTracesWidget

protected async doUpdateOpenedExperiments(): Promise<void> {
const remoteExperiments = await this._experimentManager.getOpenedExperiments();

remoteExperiments.forEach(experiment => {
this._experimentManager.addExperiment(experiment);
});

remoteExperiments.forEach(newExp => {
this.state.openedExperiments.forEach(oldExp =>{
if (newExp.UUID === oldExp.UUID) {
newExp.name = oldExp.name;
}
});
});

const selectedIndex = remoteExperiments.findIndex(experiment => this._selectedExperiment &&
experiment.UUID === this._selectedExperiment.UUID);
this.setState({ openedExperiments: remoteExperiments, selectedExperimentIndex: selectedIndex !== -1 ? selectedIndex : 0 });
signalManager().fireOpenedTracesChangedSignal(new OpenedTracesUpdatedSignalPayload(remoteExperiments ? remoteExperiments.length : 0));
}

protected handleTabNameUpdate(tabEditingOpen: string, index: number): void {
const exp = this.state.openedExperiments[index];
exp.name = tabEditingOpen;
const experimentOpenNew = this.state.openedExperiments;
experimentOpenNew[index] = exp;
this.setState({openedExperiments: experimentOpenNew});
}

protected handleShareButtonClick = (index: number): void => this.doHandleShareButtonClick(index);

protected doHandleShareButtonClick(index: number): void {
Expand Down
4 changes: 4 additions & 0 deletions packages/react-components/style/trace-explorer.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
background-color: var(--theia-selection-background);
}

.edit-trace-name {
display: inline-block;
}

/* Share options have been commented out, grid is disabled to optimize horizontal space */
/* .trace-element-container {
display: grid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export class TraceViewerWidget extends ReactWidget {
private onOutputAdded = (payload: OutputAddedSignalPayload): void => this.doHandleOutputAddedSignal(payload);
private onExperimentSelected = (experiment: Experiment): void => this.doHandleExperimentSelectedSignal(experiment);
private onCloseExperiment = (UUID: string): void => this.doHandleCloseExperimentSignal(UUID);
private onTabNameChange = (payload: { tabName: string, expirementUUID: string; }): void => this.doHandleTabeNameChange(payload);

@inject(TraceViewerWidgetOptions) protected readonly options: TraceViewerWidgetOptions;
@inject(TspClientProvider) protected tspClientProvider: TspClientProvider;
Expand Down Expand Up @@ -111,13 +112,20 @@ export class TraceViewerWidget extends ReactWidget {
signalManager().on(Signals.OUTPUT_ADDED, this.onOutputAdded);
signalManager().on(Signals.EXPERIMENT_SELECTED, this.onExperimentSelected);
signalManager().on(Signals.CLOSE_TRACEVIEWERTAB, this.onCloseExperiment);
signalManager().on(Signals.TABNAME_CHANGED, this.onTabNameChange);
}

protected updateBackgroundTheme(): void {
const currentThemeType = ThemeService.get().getCurrentTheme().type;
signalManager().fireThemeChangedSignal(currentThemeType);
}

protected doHandleTabeNameChange(payload: { tabName: string, expirementUUID: string; }): void {
if (payload.expirementUUID === this.options.traceUUID) {
this.title.label = payload.tabName;
}
}

dispose(): void {
super.dispose();
signalManager().off(Signals.OUTPUT_ADDED, this.onOutputAdded);
Expand Down

0 comments on commit 33d7ea8

Please sign in to comment.