Merge branch 'develop'

This commit is contained in:
Alice Gaudon 2020-01-21 13:09:21 +01:00
commit 2edffba81e
7 changed files with 271 additions and 56 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "tabs", "name": "tabs",
"version": "0.3.0", "version": "0.4.0",
"description": "Persistent and separate browser tabs in one window", "description": "Persistent and separate browser tabs in one window",
"author": { "author": {
"name": "Alice Gaudon", "name": "Alice Gaudon",

View File

@ -21,6 +21,7 @@
</div> </div>
<ul id="service-selector"></ul> <ul id="service-selector"></ul>
<div id="service-last-drag-position" class="hidden"></div>
<button id="add-button"><i class="fa fa-plus"></i></button> <button id="add-button"><i class="fa fa-plus"></i></button>

View File

@ -8,6 +8,7 @@ const {
dialog, dialog,
} = remote; } = remote;
const appInfo = {};
const icons = []; const icons = [];
let services = []; let services = [];
@ -18,24 +19,38 @@ let addButton;
// Service context menu // Service context menu
const serviceContextMenu = new Menu(); function openServiceContextMenu(event, serviceId) {
serviceContextMenu.append(new MenuItem({ event.preventDefault();
label: 'Reload', click: () => { const service = services[serviceId];
reloadService(serviceContextMenu.serviceId);
} const menu = new Menu();
})); const ready = service.view && service.viewReady, notReady = !service.view && !service.viewReady;
serviceContextMenu.append(new MenuItem({ menu.append(new MenuItem({
label: 'Home', click: () => {
service.view.loadURL(service.url)
.catch(console.error);
},
enabled: ready,
}));
menu.append(new MenuItem({
label: ready ? 'Reload' : 'Load', click: () => {
reloadService(serviceId);
},
enabled: ready || notReady,
}));
menu.append(new MenuItem({
label: 'Close', click: () => { label: 'Close', click: () => {
unloadService(serviceContextMenu.serviceId); unloadService(serviceId);
} },
})); enabled: ready,
serviceContextMenu.append(new MenuItem({type: "separator"})); }));
serviceContextMenu.append(new MenuItem({ menu.append(new MenuItem({type: "separator"}));
menu.append(new MenuItem({
label: 'Edit', click: () => { label: 'Edit', click: () => {
ipcRenderer.send('openServiceSettings', serviceContextMenu.serviceId); ipcRenderer.send('openServiceSettings', serviceId);
} }
})); }));
serviceContextMenu.append(new MenuItem({ menu.append(new MenuItem({
label: 'Delete', click: () => { label: 'Delete', click: () => {
dialog.showMessageBox(remote.getCurrentWindow(), { dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'question', type: 'question',
@ -45,20 +60,20 @@ serviceContextMenu.append(new MenuItem({
cancelId: 0, cancelId: 0,
}).then(result => { }).then(result => {
if (result.response === 1) { if (result.response === 1) {
ipcRenderer.send('deleteService', serviceContextMenu.serviceId); ipcRenderer.send('deleteService', serviceId);
} }
}).catch(console.error); }).catch(console.error);
} }
})); }));
menu.popup({window: remote.getCurrentWindow()});
function openServiceContextMenu(event, index) {
event.preventDefault();
serviceContextMenu.serviceId = index;
serviceContextMenu.popup({window: remote.getCurrentWindow()});
} }
ipcRenderer.on('data', (event, brandIcons, solidIcons, actualServices, actualSelectedService) => { ipcRenderer.on('data', (event, appData, brandIcons, solidIcons, actualServices, actualSelectedService) => {
// App info
appInfo.title = appData.title;
// Icons
for (const icon of brandIcons) { for (const icon of brandIcons) {
icons.push(icon); icons.push(icon);
} }
@ -81,6 +96,19 @@ ipcRenderer.on('data', (event, brandIcons, solidIcons, actualServices, actualSel
createService(i); createService(i);
} }
// Init drag last position
const lastDragPosition = document.getElementById('service-last-drag-position');
lastDragPosition.addEventListener('dragover', () => {
const index = services.length;
if (draggedId !== index && draggedId !== index - 1) {
resetDrag();
lastDragTarget = dragTargetId = index;
lastDragPosition.classList.remove('hidden');
lastDragPosition.classList.add('drag-target');
}
});
// Set active service
if (actualSelectedService < 0 || actualSelectedService >= services.length) { if (actualSelectedService < 0 || actualSelectedService >= services.length) {
actualSelectedService = 0; actualSelectedService = 0;
} }
@ -114,6 +142,31 @@ ipcRenderer.on('updateService', (e, id, data) => {
} }
}); });
ipcRenderer.on('reorderService', (e, serviceId, targetId) => {
const oldServices = services;
services = [];
for (let i = 0; i < targetId; i++) {
if (i !== serviceId) {
services.push(oldServices[i]);
}
}
services.push(oldServices[serviceId]);
const newId = services.length - 1;
for (let i = targetId; i < oldServices.length; i++) {
if (i !== serviceId) {
services.push(oldServices[i]);
}
}
document.getElementById('service-selector').innerHTML = '';
for (let i = 0; i < services.length; i++) {
services[i].li = undefined;
createService(i);
}
setActiveService(newId);
});
ipcRenderer.on('deleteService', (e, id) => { ipcRenderer.on('deleteService', (e, id) => {
const nav = document.querySelector('#service-selector'); const nav = document.querySelector('#service-selector');
@ -184,6 +237,55 @@ function createService(index, nextNavButton) {
if (service.autoLoad) { if (service.autoLoad) {
loadService(index, service); loadService(index, service);
} }
initDrag(index, li);
}
let draggedId;
let lastDragTarget = -1;
let dragTargetId = -1;
let dragTargetCount = 0;
function initDrag(index, li) {
li.serviceId = index;
li.draggable = true;
li.addEventListener('dragstart', (event) => {
draggedId = index;
event.dataTransfer.dropEffect = 'move';
document.getElementById('service-last-drag-position').classList.remove('hidden');
});
li.addEventListener('dragover', () => {
if (draggedId !== index && draggedId !== index - 1) {
resetDrag();
lastDragTarget = dragTargetId = index;
document.getElementById('service-last-drag-position').classList.remove('hidden');
li.classList.add('drag-target');
}
});
li.addEventListener('dragend', () => {
reorderService(draggedId, lastDragTarget);
resetDrag();
});
}
function resetDrag() {
lastDragTarget = -1;
dragTargetId = -1;
dragTargetCount = 0;
document.getElementById('service-selector').querySelectorAll('li').forEach(li => {
li.classList.remove('drag-target');
});
const lastDragPosition = document.getElementById('service-last-drag-position');
lastDragPosition.classList.remove('drag-target');
lastDragPosition.classList.add('hidden');
}
function reorderService(serviceId, targetId) {
console.log('Reordering service', serviceId, targetId);
if (targetId >= 0) {
setActiveService(null);
ipcRenderer.send('reorderService', serviceId, targetId);
}
} }
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
@ -200,7 +302,9 @@ document.addEventListener('DOMContentLoaded', () => {
function setActiveService(serviceId) { function setActiveService(serviceId) {
const currentService = services[serviceId]; const currentService = services[serviceId];
process.nextTick(() => { process.nextTick(() => {
if (currentService) {
loadService(serviceId, currentService); loadService(serviceId, currentService);
}
// Hide previous service // Hide previous service
if (services[selectedService] && services[selectedService].view) { if (services[selectedService] && services[selectedService].view) {
@ -208,7 +312,9 @@ function setActiveService(serviceId) {
} }
// Show service // Show service
if (currentService) {
currentService.view.classList.add('active'); currentService.view.classList.add('active');
}
// Save active service ID // Save active service ID
selectedService = serviceId; selectedService = serviceId;
@ -322,6 +428,16 @@ function updateNavigation() {
if (view && view.canGoBack()) backButton.classList.remove('disabled'); if (view && view.canGoBack()) backButton.classList.remove('disabled');
else backButton.classList.add('disabled'); else backButton.classList.add('disabled');
} }
updateWindowTitle();
}
function updateWindowTitle() {
if (selectedService === null) {
ipcRenderer.send('updateWindowTitle', null);
} else {
ipcRenderer.send('updateWindowTitle', selectedService, services[selectedService].view.getWebContents().getTitle());
}
} }
function goForward() { function goForward() {

View File

@ -55,9 +55,11 @@ webContents.on('context-menu', (event, props) => {
// Text clipboard // Text clipboard
if (editFlags.canUndo || editFlags.canRedo || editFlags.canCut || editFlags.canCopy || editFlags.canPaste || editFlags.canDelete) { if (editFlags.canUndo || editFlags.canRedo || editFlags.canCut || editFlags.canCopy || editFlags.canPaste || editFlags.canDelete) {
if (editFlags.canUndo || editFlags.canRedo) {
if (menu.items.length > 0) { if (menu.items.length > 0) {
menu.append(new MenuItem({type: 'separator'})); menu.append(new MenuItem({type: 'separator'}));
} }
if (editFlags.canUndo) { if (editFlags.canUndo) {
menu.append(new MenuItem({ menu.append(new MenuItem({
label: 'Undo', label: 'Undo',
@ -70,10 +72,12 @@ webContents.on('context-menu', (event, props) => {
role: 'redo', role: 'redo',
})); }));
} }
}
if (menu.items.length > 0) { if (menu.items.length > 0) {
menu.append(new MenuItem({type: 'separator'})); menu.append(new MenuItem({type: 'separator'}));
} }
menu.append(new MenuItem({ menu.append(new MenuItem({
label: 'Cut', label: 'Cut',
role: 'cut', role: 'cut',

View File

@ -10,6 +10,51 @@ body {
padding: 0; padding: 0;
} }
#service-selector [draggable] {
user-select: none;
background-color: rgb(43, 43, 43);
}
#service-selector [draggable] img {
-webkit-user-drag: none;
user-drag: none;
}
#service-selector .drag-target button {
height: 144px;
padding-top: 88px;
}
#service-selector .drag-target button::after,
#service-last-drag-position.drag-target {
content: "";
height: 72px;
border: 1px dashed #fff;
box-sizing: border-box;
background-color: rgb(43, 43, 43);
}
#service-selector .drag-target button::after {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
#service-last-drag-position:not(.hidden):not(.drag-target) {
display: block;
padding: 16px 4px;
background-color: #fff5;
}
#service-last-drag-position:not(.drag-target)::after {
content: "";
display: block;
border-bottom: 1px solid #fff;
}
#service-selector .drag-target::after {
top: 75% !important;
}
*:focus { *:focus {
outline-color: rgb(118, 93, 176); outline-color: rgb(118, 93, 176);
} }

View File

@ -1,12 +1,29 @@
export default class Meta { export default class Meta {
static #title = 'Tabs';
static #devMode = null; static #devMode = null;
static get title() {
return this.#title;
}
static isDevMode() { static isDevMode() {
if(this.#devMode === null) { if (this.#devMode === null) {
this.#devMode = process.argv.length > 2 && process.argv[2] === '--dev'; this.#devMode = process.argv.length > 2 && process.argv[2] === '--dev';
console.debug('Dev mode:', this.#devMode); console.debug('Dev mode:', this.#devMode);
} }
return this.#devMode; return this.#devMode;
} }
/**
* @param service {Service}
* @param viewTitle {string}
*/
static getTitleForService(service, viewTitle) {
let suffix = '';
if (typeof viewTitle === 'string' && viewTitle.length > 0) {
suffix = ' - ' + viewTitle;
}
return this.title + ' - ' + service.name + suffix;
}
} }

View File

@ -58,6 +58,7 @@ function createWindow() {
}, },
autoHideMenuBar: true, autoHideMenuBar: true,
icon: iconPath, icon: iconPath,
title: Meta.title,
}); });
window.maximize(); window.maximize();
window.on('closed', () => { window.on('closed', () => {
@ -157,12 +158,43 @@ function createWindow() {
window.webContents.send('deleteService', id); window.webContents.send('deleteService', id);
}); });
ipcMain.on('reorderService', (e, serviceId, targetId) => {
console.log('Reordering services', serviceId, targetId);
const oldServices = config.services;
config.services = [];
for (let i = 0; i < targetId; i++) {
if (i !== serviceId) {
config.services.push(oldServices[i]);
}
}
config.services.push(oldServices[serviceId]);
for (let i = targetId; i < oldServices.length; i++) {
if (i !== serviceId) {
config.services.push(oldServices[i]);
}
}
e.reply('reorderService', serviceId, targetId);
config.save();
});
ipcMain.on('updateWindowTitle', (event, serviceId, viewTitle) => {
if (serviceId === null) {
window.setTitle(Meta.title);
} else {
const service = config.services[serviceId];
window.setTitle(Meta.getTitleForService(service, viewTitle));
}
});
console.log('> App started'); console.log('> App started');
} }
function sendData() { function sendData() {
console.log('Syncing data'); console.log('Syncing data');
window.webContents.send('data', brandIcons, solidIcons, config.services, selectedService); window.webContents.send('data', Meta.title, brandIcons, solidIcons, config.services, selectedService);
} }
function setActiveService(index) { function setActiveService(index) {