Skip to content

Commit

Permalink
Implemented Toolbar support for sidepanels and changed sidepanel tabs.
Browse files Browse the repository at this point in the history
Every sidepanel has an header now where the title and a toolbar
is shown (if tools are registered for that widget).
As an example the control buttons of search in workspace extension are
moved to the toolbar.

Additionally the tabs of sidebars  don't show labels anymore but icons.

Fixes #3864
  • Loading branch information
jbicker committed Mar 18, 2019
1 parent 8ff594a commit 71a0433
Show file tree
Hide file tree
Showing 22 changed files with 383 additions and 110 deletions.
4 changes: 3 additions & 1 deletion packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ import { LocalStorageService, StorageService } from './storage-service';
import { WidgetFactory, WidgetManager } from './widget-manager';
import {
ApplicationShell, ApplicationShellOptions, DockPanelRenderer, TabBarRenderer,
TabBarRendererFactory, ShellLayoutRestorer, SidePanelHandler, SidePanelHandlerFactory, SplitPositionHandler, DockPanelRendererFactory
TabBarRendererFactory, ShellLayoutRestorer,
SidePanelHandler, SidePanelHandlerFactory,
SplitPositionHandler, DockPanelRendererFactory
} from './shell';
import { StatusBar, StatusBarImpl } from './status-bar/status-bar';
import { LabelParser } from './label-parser';
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/browser/icons/debug-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/core/src/browser/icons/extensions-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/core/src/browser/icons/files-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/core/src/browser/icons/scm-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions packages/core/src/browser/icons/search-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 28 additions & 5 deletions packages/core/src/browser/shell/side-panel-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { TabBarRendererFactory, TabBarRenderer, SHELL_TABBAR_CONTEXT_MENU, SideT
import { SplitPositionHandler, SplitPositionOptions } from './split-panels';
import { FrontendApplicationStateService } from '../frontend-application-state';
import { TheiaDockPanel } from './theia-dock-panel';
import { SidePanelToolbar } from './side-panel-toolbar';
import { TabBarToolbarRegistry, TabBarToolbarFactory, TabBarToolbar } from './tab-bar-toolbar';

/** The class name added to the left and right area panels. */
export const LEFT_RIGHT_AREA_CLASS = 'theia-app-sides';
Expand Down Expand Up @@ -56,6 +58,10 @@ export class SidePanelHandler {
* tab bar itself remains visible as long as there is at least one widget.
*/
tabBar: SideTabBar;
/**
* A tool bar, which displays a title and widget specific command buttons.
*/
toolBar: SidePanelToolbar;
/**
* The widget container is a dock panel in `single-document` mode, which means that the panel
* cannot be split.
Expand Down Expand Up @@ -85,6 +91,8 @@ export class SidePanelHandler {
*/
protected options: SidePanel.Options;

@inject(TabBarToolbarRegistry) protected tabBarToolBarRegistry: TabBarToolbarRegistry;
@inject(TabBarToolbarFactory) protected tabBarToolBarFactory: () => TabBarToolbar;
@inject(TabBarRendererFactory) protected tabBarRendererFactory: () => TabBarRenderer;
@inject(SplitPositionHandler) protected splitPositionHandler: SplitPositionHandler;
@inject(FrontendApplicationStateService) protected readonly applicationStateService: FrontendApplicationStateService;
Expand All @@ -96,6 +104,7 @@ export class SidePanelHandler {
this.side = side;
this.options = options;
this.tabBar = this.createSideBar();
this.toolBar = this.createToolbar();
this.dockPanel = this.createSidePanel();
this.container = this.createContainer();

Expand Down Expand Up @@ -152,7 +161,19 @@ export class SidePanelHandler {
return sidePanel;
}

protected createToolbar(): SidePanelToolbar {
const toolbar = new SidePanelToolbar(this.tabBarToolBarRegistry, this.tabBarToolBarFactory, this.side);
return toolbar;
}

protected createContainer(): Panel {
const contentBox = new BoxLayout({ direction: 'top-to-bottom', spacing: 0 });
BoxPanel.setStretch(this.toolBar, 0);
contentBox.addWidget(this.toolBar);
BoxPanel.setStretch(this.dockPanel, 1);
contentBox.addWidget(this.dockPanel);
const contentPanel = new BoxPanel({layout: contentBox});

const side = this.side;
let direction: BoxLayout.Direction;
switch (side) {
Expand All @@ -165,12 +186,12 @@ export class SidePanelHandler {
default:
throw new Error('Illegal argument: ' + side);
}
const boxLayout = new BoxLayout({ direction, spacing: 0 });
const containerLayout = new BoxLayout({ direction, spacing: 0 });
BoxPanel.setStretch(this.tabBar, 0);
boxLayout.addWidget(this.tabBar);
BoxPanel.setStretch(this.dockPanel, 1);
boxLayout.addWidget(this.dockPanel);
const boxPanel = new BoxPanel({ layout: boxLayout });
containerLayout.addWidget(this.tabBar);
BoxPanel.setStretch(contentPanel, 1);
containerLayout.addWidget(contentPanel);
const boxPanel = new BoxPanel({ layout: containerLayout });
boxPanel.id = 'theia-' + side + '-content-panel';
return boxPanel;
}
Expand Down Expand Up @@ -327,6 +348,8 @@ export class SidePanelHandler {
const hideDockPanel = currentTitle === null;
let relativeSizes: number[] | undefined;

this.toolBar.toolbarTitle = currentTitle || undefined;

if (hideDockPanel) {
container.addClass(COLLAPSED_CLASS);
if (this.state.expansion === SidePanel.ExpansionState.expanded && !this.state.empty) {
Expand Down
91 changes: 91 additions & 0 deletions packages/core/src/browser/shell/side-panel-toolbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/********************************************************************************
* Copyright (C) 2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { Widget, Title } from "@phosphor/widgets";
import { TabBarToolbar, TabBarToolbarRegistry } from "@theia/core/src/browser/shell/tab-bar-toolbar";
import { Message } from "@phosphor/messaging";

export class SidePanelToolbar extends Widget {

protected titleContainer: HTMLElement | undefined;
private _toolbarTitle: Title<Widget> | undefined;
protected toolbar: TabBarToolbar | undefined;

constructor(
protected readonly tabBarToolbarRegistry: TabBarToolbarRegistry,
protected readonly tabBarToolbarFactory: () => TabBarToolbar,
protected readonly side: 'left' | 'right') {
super();
this.init();
}

protected onAfterAttach(msg: Message): void {
if (this.toolbar) {
if (this.toolbar.isAttached) {
Widget.detach(this.toolbar);
}
Widget.attach(this.toolbar, this.node);
}
super.onAfterAttach(msg);
}

protected onBeforeDetach(msg: Message): void {
if (this.titleContainer) {
this.node.removeChild(this.titleContainer);
}
if (this.toolbar && this.toolbar.isAttached) {
Widget.detach(this.toolbar);
}
super.onBeforeDetach(msg);
}

protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
this.updateToolbar();
}

protected updateToolbar(): void {
if (!this.toolbar) {
return;
}
const current = this._toolbarTitle;
const widget = current && current.owner || undefined;

const items = widget ? this.tabBarToolbarRegistry.visibleItems(widget) : [];
items.forEach(i => i.onDidChange && i.onDidChange(() => {
this.update();
}));
this.toolbar.updateItems(items, widget);
}

protected init(): void {
this.titleContainer = document.createElement('div');
this.titleContainer.classList.add('theia-sidepanel-title');
this.node.appendChild(this.titleContainer);
this.node.classList.add('theia-sidepanel-toolbar');
this.node.classList.add(`theia-${this.side}-side-panel`);
this.toolbar = this.tabBarToolbarFactory();
this.update();
}

set toolbarTitle(title: Title<Widget> | undefined) {
if (this.titleContainer && title) {
this._toolbarTitle = title;
this.titleContainer.innerHTML = this._toolbarTitle.label;
this.update();
}
}
}
16 changes: 12 additions & 4 deletions packages/core/src/browser/shell/tab-bar-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,21 @@ export class TabBarToolbar extends ReactWidget {
}
}
const command = this.commands.getCommand(item.command);
const iconClass = command && command.iconClass;
if (iconClass) {
classNames.push(iconClass);
if (command) {
const iconClass = command.iconClass;
if (iconClass) {
classNames.push(iconClass);
}
}
return <div key={item.id} className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM} >
return <div key={item.id} className={`${TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM}${command && this.commandIsEnabled(command.id) ? ' enabled' : ''}`} >
<div id={item.id} className={classNames.join(' ')} onClick={this.executeCommand} title={item.tooltip}>{innerText}</div>
</div>;
}

protected commandIsEnabled(command: string) {
return this.commands.isEnabled(command, this.current);
}

protected executeCommand = (e: React.MouseEvent<HTMLElement>) => {
const item = this.items.get(e.currentTarget.id);
if (item) {
Expand Down Expand Up @@ -174,6 +180,8 @@ export interface TabBarToolbarItem {
*/
readonly when?: string;

onDidChange?: (handler: () => void) => void;

}

export namespace TabBarToolbarItem {
Expand Down
63 changes: 50 additions & 13 deletions packages/core/src/browser/style/sidepanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
|----------------------------------------------------------------------------*/

:root {
--theia-private-sidebar-tab-width: 32px;
--theia-private-sidebar-tab-width: 50px;
--theia-private-sidebar-tab-height: 32px;
--theia-private-sidebar-scrollbar-rail-width: 7px;
--theia-private-sidebar-scrollbar-width: 5px;
}
Expand All @@ -34,7 +35,7 @@
color: var(--theia-ui-font-color1);
background: var(--theia-layout-color2);
font-size: var(--theia-ui-font-size1);
min-width: var(--theia-private-sidebar-tab-width);
min-width: var(--theia-private-sidebar-tab-width);
max-width: var(--theia-private-sidebar-tab-width);
}

Expand All @@ -44,9 +45,12 @@

.p-TabBar.theia-app-sides .p-TabBar-tab {
position: relative;
padding: 12px 8px;
padding: 11px 10px;
background: var(--theia-layout-color2);
flex-direction: column;
justify-content: center;
align-items: center;
min-height: var(--theia-private-sidebar-tab-height);
}

.p-TabBar.theia-app-left .p-TabBar-tab {
Expand All @@ -60,6 +64,9 @@
.p-TabBar.theia-app-sides .p-TabBar-tab.p-mod-current {
color: var(--theia-ui-font-color0);
background: var(--theia-layout-color0);
min-height: var(--theia-private-sidebar-tab-height);
height: var(--theia-private-sidebar-tab-height);
border-top: none;
}

.p-TabBar.theia-app-left .p-TabBar-tab.p-mod-current.theia-mod-active {
Expand All @@ -76,21 +83,21 @@
background: var(--theia-accent-color3);
}

.p-TabBar.theia-app-sides .p-TabBar-tabIcon,
.p-TabBar.theia-app-sides .p-TabBar-tabLabel,
.p-TabBar.theia-app-sides .p-TabBar-tabCloseIcon {
display: none;
}

.p-TabBar.theia-app-sides .p-TabBar-tabLabel {
position: absolute;
min-height: var(--theia-private-sidebar-tab-width);
max-height: var(--theia-private-sidebar-tab-width);
align-items: flex-start;
}

.p-TabBar.theia-app-sides .p-TabBar-tabIcon {
transform: scale(1.5);
max-height: 15px;
width: 30px;
font-size: 23px;
text-align: center;
height: 28px;
background-repeat: no-repeat;
display: flex;
align-items: center;
justify-content: center;
background-size: 26px
}

.p-TabBar.theia-app-left .p-TabBar-tabLabel {
Expand Down Expand Up @@ -173,3 +180,33 @@
.p-TabBar.theia-app-right > .theia-TabBar-hidden-content .p-TabBar-tabLabel {
transform: none;
}

/*-----------------------------------------------------------------------------
| Sidepanel Toolbar
|----------------------------------------------------------------------------*/

.theia-sidepanel-toolbar {
min-height: 30px;
display: flex;
padding-left: 5px;
align-items: center;
}

.theia-sidepanel-toolbar.theia-left-side-panel {
border-right: var(--theia-panel-border-width) solid var(--theia-border-color1);
}

.theia-sidepanel-toolbar.theia-right-side-panel {
border-left: var(--theia-panel-border-width) solid var(--theia-border-color1);
}

.theia-sidepanel-toolbar .theia-sidepanel-title {
color: var(--theia-ui-font-color2);
flex: 1;
}

.theia-sidepanel-toolbar .p-TabBar-toolbar .item > div{
height: 18px;
width: 18px;
background-repeat: no-repeat;
}
25 changes: 21 additions & 4 deletions packages/core/src/browser/style/tabs.css
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,16 @@
}

.p-TabBar .p-TabBar-tabIcon {
width: 15px;
line-height: 1.7;
width: 15px;
line-height: 1.7;
font-size: 12px;
text-align: center;
background-repeat: no-repeat;
}

.p-TabBar.theia-app-centers .p-TabBar-tabIcon {
background-size: 13px;
background-position-y: 3px;
}


Expand Down Expand Up @@ -207,8 +215,17 @@
display: flex;
align-items: center;
margin-left: 8px; /* `padding` + `margin-right` from the container toolbar */
opacity: 0.25;
cursor: default;
}

.p-TabBar-toolbar .item:hover {
cursor: pointer;
.p-TabBar-toolbar .item.enabled {
opacity: 1.0;
cursor: pointer;
}

.p-TabBar-toolbar .item > div{
height: 18px;
width: 18px;
background-repeat: no-repeat;
}
Loading

0 comments on commit 71a0433

Please sign in to comment.