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",
"version": "0.3.0",
"version": "0.4.0",
"description": "Persistent and separate browser tabs in one window",
"author": {
"name": "Alice Gaudon",

View File

@ -21,6 +21,7 @@
</div>
<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>

View File

@ -8,6 +8,7 @@ const {
dialog,
} = remote;
const appInfo = {};
const icons = [];
let services = [];
@ -18,47 +19,61 @@ let addButton;
// Service context menu
const serviceContextMenu = new Menu();
serviceContextMenu.append(new MenuItem({
label: 'Reload', click: () => {
reloadService(serviceContextMenu.serviceId);
}
}));
serviceContextMenu.append(new MenuItem({
label: 'Close', click: () => {
unloadService(serviceContextMenu.serviceId);
}
}));
serviceContextMenu.append(new MenuItem({type: "separator"}));
serviceContextMenu.append(new MenuItem({
label: 'Edit', click: () => {
ipcRenderer.send('openServiceSettings', serviceContextMenu.serviceId);
}
}));
serviceContextMenu.append(new MenuItem({
label: 'Delete', click: () => {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'question',
title: 'Confirm',
message: 'Are you sure you want to delete this service?',
buttons: ['Cancel', 'Confirm'],
cancelId: 0,
}).then(result => {
if (result.response === 1) {
ipcRenderer.send('deleteService', serviceContextMenu.serviceId);
}
}).catch(console.error);
}
}));
function openServiceContextMenu(event, index) {
function openServiceContextMenu(event, serviceId) {
event.preventDefault();
serviceContextMenu.serviceId = index;
serviceContextMenu.popup({window: remote.getCurrentWindow()});
const service = services[serviceId];
const menu = new Menu();
const ready = service.view && service.viewReady, notReady = !service.view && !service.viewReady;
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: () => {
unloadService(serviceId);
},
enabled: ready,
}));
menu.append(new MenuItem({type: "separator"}));
menu.append(new MenuItem({
label: 'Edit', click: () => {
ipcRenderer.send('openServiceSettings', serviceId);
}
}));
menu.append(new MenuItem({
label: 'Delete', click: () => {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'question',
title: 'Confirm',
message: 'Are you sure you want to delete this service?',
buttons: ['Cancel', 'Confirm'],
cancelId: 0,
}).then(result => {
if (result.response === 1) {
ipcRenderer.send('deleteService', serviceId);
}
}).catch(console.error);
}
}));
menu.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) {
icons.push(icon);
}
@ -81,6 +96,19 @@ ipcRenderer.on('data', (event, brandIcons, solidIcons, actualServices, actualSel
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) {
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) => {
const nav = document.querySelector('#service-selector');
@ -184,6 +237,55 @@ function createService(index, nextNavButton) {
if (service.autoLoad) {
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', () => {
@ -200,7 +302,9 @@ document.addEventListener('DOMContentLoaded', () => {
function setActiveService(serviceId) {
const currentService = services[serviceId];
process.nextTick(() => {
loadService(serviceId, currentService);
if (currentService) {
loadService(serviceId, currentService);
}
// Hide previous service
if (services[selectedService] && services[selectedService].view) {
@ -208,7 +312,9 @@ function setActiveService(serviceId) {
}
// Show service
currentService.view.classList.add('active');
if (currentService) {
currentService.view.classList.add('active');
}
// Save active service ID
selectedService = serviceId;
@ -322,6 +428,16 @@ function updateNavigation() {
if (view && view.canGoBack()) backButton.classList.remove('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() {

View File

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

View File

@ -10,6 +10,51 @@ body {
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 {
outline-color: rgb(118, 93, 176);
}

View File

@ -1,12 +1,29 @@
export default class Meta {
static #title = 'Tabs';
static #devMode = null;
static get title() {
return this.#title;
}
static isDevMode() {
if(this.#devMode === null) {
if (this.#devMode === null) {
this.#devMode = process.argv.length > 2 && process.argv[2] === '--dev';
console.debug('Dev mode:', 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,
icon: iconPath,
title: Meta.title,
});
window.maximize();
window.on('closed', () => {
@ -157,12 +158,43 @@ function createWindow() {
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');
}
function sendData() {
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) {