Skip to content
This repository has been archived by the owner on Jun 23, 2021. It is now read-only.

Commit

Permalink
feat: add tab button to duplicate currently selected window (#104)
Browse files Browse the repository at this point in the history
* feat: initial tabs adding mechanism

* fix: add tab button space

* fix: add tab icon
  • Loading branch information
sentialx authored Apr 16, 2020
1 parent ea91121 commit 27104f6
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 107 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
},
"dependencies": {
"iohook": "^0.6.5",
"node-window-manager": "^2.2.0",
"node-window-manager": "^2.2.1",
"extract-file-icon": "0.3.2"
},
"iohook": {
Expand Down
34 changes: 19 additions & 15 deletions src/main/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ export class Container {

private appWindow: AppWindow;

public constructor(appWindow: AppWindow, window: ProcessWindow) {
public constructor(
appWindow: AppWindow,
window: ProcessWindow,
dragged = true,
) {
this.appWindow = appWindow;

const colId = spaceId++;
Expand All @@ -50,7 +54,7 @@ export class Container {

window.rowId = rowId;
window.columnId = colId;
window.dragged = true;
window.dragged = dragged;

this.windows.push(window);

Expand Down Expand Up @@ -88,7 +92,7 @@ export class Container {
top += row.height;

const window = this.windows.find(
x => x.rowId === row.id && x.columnId === col.id,
(x) => x.rowId === row.id && x.columnId === col.id,
);

if (window && !window.dragged && !window.resizing) {
Expand All @@ -107,26 +111,26 @@ export class Container {
}

public removeWindow(id: number, mouseup = true) {
const win = this.windows.find(x => x.id === id);
const win = this.windows.find((x) => x.id === id);
if (!win) return;

const col = this.columns.find(x => x.id === win.columnId);
const col = this.columns.find((x) => x.id === win.columnId);
if (!col) return;

let winDetached = false;

if (col.rows.length === 1) {
this.columns = this.columns.filter(x => x.id !== win.columnId);
this.columns = this.columns.filter((x) => x.id !== win.columnId);
this.detachWindow(win, mouseup);
winDetached = true;
}

if (!col) return;

const row = col.rows.find(x => x.id === win.rowId);
const row = col.rows.find((x) => x.id === win.rowId);
if (!row) return;

col.rows = col.rows.filter(x => x.id !== win.rowId);
col.rows = col.rows.filter((x) => x.id !== win.rowId);
if (!winDetached) {
this.detachWindow(win, mouseup);
}
Expand All @@ -147,7 +151,7 @@ export class Container {

window.detach(mouseup);

this.windows = this.windows.filter(x => x.id !== window.id);
this.windows = this.windows.filter((x) => x.id !== window.id);

if (this.windows.length === 0) {
this.appWindow.removeContainer(this);
Expand All @@ -156,15 +160,15 @@ export class Container {

public dragWindow(window: ProcessWindow, { x, y, type }: any) {
const area = this.appWindow.getContentArea();
const win = this.windows.find(x => x.id === window.id);
const win = this.windows.find((x) => x.id === window.id);

if (win) {
if (win.resizing) return;

const col = this.columns.find(x => x.id === win.columnId);
const col = this.columns.find((x) => x.id === win.columnId);
if (!col) return;

const row = col.rows.find(x => x.id === win.rowId);
const row = col.rows.find((x) => x.id === win.rowId);

win.dragged = true;

Expand Down Expand Up @@ -261,10 +265,10 @@ export class Container {
}

public resizeWindow(window: ProcessWindow, resizeWinCb: () => void) {
const win = this.windows.find(x => x.id === window.id);
const win = this.windows.find((x) => x.id === window.id);
if (!win) return;

const col = this.columns.find(x => x.id === win.columnId);
const col = this.columns.find((x) => x.id === win.columnId);
if (!col) return;

const winBounds = win.getBounds();
Expand All @@ -290,7 +294,7 @@ export class Container {
c.weight = c.width / baseWidth;
}
} else if (winBounds.height !== win.lastBounds.height) {
const row = col.rows.find(x => x.id === win.rowId);
const row = col.rows.find((x) => x.id === win.rowId);
if (!row) return;

const index = winBounds.y !== win.lastBounds.y ? -1 : 1;
Expand Down
141 changes: 92 additions & 49 deletions src/main/windows/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ export class AppWindow extends BrowserWindow {
this.selectContainer(this.containers.find((x) => x.id === id));
});

ipcMain.on('new-tab', () => {
this.newTab();
});

ipcMain.on('detach-window', (e, id: number) => {
for (const container of this.containers) {
container.removeWindow(id, true);
Expand Down Expand Up @@ -241,29 +245,7 @@ export class AppWindow extends BrowserWindow {
this.draggedWindow.resizing = false;

if (this.willAttachWindow) {
const win = this.draggedWindow;
const container = this.draggedContainer;

if (platform() === 'win32') {
const handle = this.getNativeWindowHandle().readInt32LE(0);
win.setOwner(handle);
}

if (platform() === 'darwin' && this.containers.length === 0) {
this.setBounds({ height: TOOLBAR_HEIGHT } as any);
this.setMaximumSize(0, TOOLBAR_HEIGHT);
}

this.containers.push(this.draggedContainer);
this.willAttachWindow = false;

this.draggedContainer.rearrangeWindows();

setTimeout(() => {
this.selectContainer(container);
}, 50);

this.controlShortcuts(win.id);
this.attachNewContainer(this.draggedContainer, this.draggedWindow);
} else if (this.willSplitWindow && !this.detached) {
this.willSplitWindow = false;

Expand Down Expand Up @@ -420,34 +402,9 @@ export class AppWindow extends BrowserWindow {
this.selectedContainer.removeWindow(win.id, e.type === 'mouseup');
}

const container = new Container(this, win);

const title = this.draggedWindow.getTitle();
const container = this.prepareContainer(this.draggedWindow);

this.draggedContainer = container;
win.lastTitle = title;

const icon = fileIcon(win.path, 16);

this.webContents.send('add-tab', {
id: container.id,
title,
icon,
});

try {
Vibrant.from(icon)
.getPalette()
.then((palette) => {
this.webContents.send(
'tab-background',
container.id,
palette.Vibrant.hex,
);
});
} catch (e) {
console.error(e);
}

this.draggedIn = true;
this.willAttachWindow = true;
Expand All @@ -461,6 +418,92 @@ export class AppWindow extends BrowserWindow {
}
}

public prepareContainer(window: ProcessWindow, dragged = true) {
const container = new Container(this, window, dragged);

const title = window.getTitle();
const icon = fileIcon(window.path, 16);

window.lastTitle = title;

this.webContents.send('add-tab', {
id: container.id,
title,
icon,
});

try {
Vibrant.from(icon)
.getPalette()
.then((palette) => {
this.webContents.send(
'tab-background',
container.id,
palette.Vibrant.hex,
);
});
} catch (e) {
console.error(e);
}

return container;
}

public attachNewContainer(container: Container, win: ProcessWindow) {
if (platform() === 'win32') {
const handle = this.getNativeWindowHandle().readInt32LE(0);
win.setOwner(handle);
}

if (platform() === 'darwin' && this.containers.length === 0) {
this.setBounds({ height: TOOLBAR_HEIGHT } as any);
this.setMaximumSize(0, TOOLBAR_HEIGHT);
}

this.containers.push(container);
this.willAttachWindow = false;

container.rearrangeWindows();

setTimeout(() => {
this.selectContainer(container);
}, 50);

this.controlShortcuts(win.id);
}

public async newTab() {
if (this.selectedContainer?.windows?.length === 1) {
const window = await this.createNewWindow(
this.selectedContainer.windows[0].path,
);

this.addTabWithWindow(window);
}
}

public createNewWindow(path: string): Promise<ProcessWindow> {
return new Promise((resolve, reject) => {
windowManager.createProcess(path);

const timeout = setTimeout(() => {
reject(new Error('Creating new window timed out'));
}, 5000);

windowManager.once('window-activated', (win: ProcessWindow) => {
if (win.path === path) {
clearTimeout(timeout);
resolve(new ProcessWindow(win.id, this));
}
});
});
}

public addTabWithWindow(win: ProcessWindow) {
const container = this.prepareContainer(win, false);
this.attachNewContainer(container, win);
}

public getContentArea() {
const bounds = this.getContentBounds();

Expand Down
12 changes: 11 additions & 1 deletion src/renderer/views/app/components/Tabbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ import * as React from 'react';

import HorizontalScrollbar from '../HorizontalScrollbar';
import store from '~/renderer/views/app/store';
import { StyledTabbar, TabsContainer } from './style';
import { StyledTabbar, TabsContainer, AddTab } from './style';
import { Tabs } from '../Tabs';
import { icons } from '../../constants';

const getContainer = () => store.tabsStore.containerRef.current;

const onMouseEnter = () => (store.tabsStore.scrollbarVisible = true);

const onMouseLeave = () => (store.tabsStore.scrollbarVisible = false);

const onAddTabClick = () => {
store.tabsStore.newTab();
};

export const Tabbar = observer(() => {
return (
<StyledTabbar>
Expand All @@ -22,6 +27,11 @@ export const Tabbar = observer(() => {
>
<Tabs />
</TabsContainer>
<AddTab
icon={icons.add}
onClick={onAddTabClick}
divRef={(r: any) => (store.addTab.ref = r)}
/>
<HorizontalScrollbar
ref={store.tabsStore.scrollbarRef}
enabled={store.tabsStore.scrollable}
Expand Down
49 changes: 28 additions & 21 deletions src/renderer/views/app/components/Tabbar/style.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import styled from 'styled-components';

export const StyledTabbar = styled.div`
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
transition: 0.3s opacity, 0.3s transform;
margin-right: 32px;
display: flex;
`;

export const TabsContainer = styled.div`
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
white-space: nowrap;
display: flex;
align-items: center;
`;
import styled from 'styled-components';
import { ToolbarButton } from '../ToolbarButton';
import { TOOLBAR_BUTTON_WIDTH } from '../../constants';

export const StyledTabbar = styled.div`
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
transition: 0.3s opacity, 0.3s transform;
margin-right: 32px;
display: flex;
`;

export const TabsContainer = styled.div`
height: 100%;
width: calc(100% - ${TOOLBAR_BUTTON_WIDTH}px);
position: relative;
overflow: hidden;
white-space: nowrap;
display: flex;
align-items: center;
`;

export const AddTab = styled(ToolbarButton)`
position: absolute;
left: 0;
`;
17 changes: 9 additions & 8 deletions src/renderer/views/app/constants/icons.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export const icons = {
close: require('~/shared/resources/icons/close.svg'),
dropWindow: require('~/shared/resources/icons/drop-window.svg'),
download: require('~/shared/resources/icons/download.svg'),
theme: require('~/shared/resources/icons/theme.svg'),
more: require('~/shared/resources/icons/more.svg'),
photo: require('~/shared/resources/icons/photo.svg'),
};
export const icons = {
close: require('~/shared/resources/icons/close.svg'),
add: require('~/shared/resources/icons/add.svg'),
dropWindow: require('~/shared/resources/icons/drop-window.svg'),
download: require('~/shared/resources/icons/download.svg'),
theme: require('~/shared/resources/icons/theme.svg'),
more: require('~/shared/resources/icons/more.svg'),
photo: require('~/shared/resources/icons/photo.svg'),
};
Loading

0 comments on commit 27104f6

Please sign in to comment.