Implementation of utools super panel based on electron

Time:2021-7-29

preface

In order to further improve the efficiency of development, we recently based onelectronDeveloped a comparableuToolsOpen Source Toolkitrubick。 The toolkit is not only open source, the most important thing is that it can be useduToolsAll open source plug-ins in the ecosystem! This will be a huge ability, which meansuToolsAll plug-ins in the ecosystem can be used without differentiationrubickYes.

In order to improve the user’s efficiency in designing interaction, we try to implement it againuToolsSome excellent designs in, such as:Super panel。 This function can be quickly recalled by the mouseuToolsPlug in capability without opening the application. For example, when uploading pictures, as long as we install the picture bed plug-in, when the mouse selects a picture on the desktop, we can quickly call out the menu option of uploading pictures, which is convenient and easy. Next, let’s take a look at the implementation!

Code warehouse

Rubick github

Function screenshot:

Long click right to create a folder

Implementation of utools super panel based on electron

After selecting a file, long press the right button

Implementation of utools super panel based on electron

Press and hold the right button after selecting the text

Implementation of utools super panel based on electron

Implementation principle

Get selected copy

To realize the function change, the core is to read the copy or file selected by the current user and display different functions according to the current selection. However, the core problem is how to obtain the currently selected content. I have thought about this problem for a long time. I feel that the only way to obtain the selected copy is to use itctrl + cperhapscommand + cTo copy to the clipboard first, and then throughelectron clipboardTo get the current clipboard content. howeverutoolsIt can not be realized by copying first and then long pressing, but by directly selecting text or file and long pressing to call the super panel.So be sure to get the currently selected content before right clicking and long pressing.

If you want to do this, there may be no solution. It was because you thought so before. The correct idea should be to long press and then get the selected content. Although only one of them is lost, the realization is quite different:

  1. Get the selected content first: This requires us to listen to the selected events of the native system, butelectronWith theout capability, we cannot monitor system selection events.
  2. Right click first and then get the content. The advantage is that right click first can monitor the right-click event, which is easier than selecting the event.

So you have the idea. First listen to the long press right-click event:

// macos
const mouseEvents = require("osx-mouse");

const mouseTrack = mouseEvents();
//Time to press down
let down_time = 0;

//Whether it bounces
let isPress = false;

//Monitor right click
mouseTrack.on('right-down', () => {
    isPress = true;
    down_time = Date.now();
    //Triggered after long press for 500ms
    setTimeout(async () => {
      if (isPress) {
        //Get selection
        const copyResult = await getSelectedText();
    }, 500);
})
mouseTrack.on('right-up', () => {
    isPress = false;
});

The next step is to obtain the selected content. There is a relatively simple operation to obtain the selected content, which is:

  1. adoptclipboardFirst get the current clipboard content and save a
  2. adoptrobot.jsTo call the systemcommand + cperhapsctrl + c
  3. Pass againclipboardFirst get the current clipboard content and save B
  4. Then write a to the shear board and return to B

The purpose of saving the clipboard content first is that we secretly help the user perform the copy action. After reading the content selected by the user, we need to reply to the previous clipboard content of the user. Next, let’s look at a simple implementation:

const getSelected = () => {
  return new Promise((resolve) => {
    //Cache previous copy
    const lastText = clipboard.readText('clipboard');

    const platform = process.platform;
    
    //Execute copy action
    if (platform === 'darwin') {
      robot.keyTap('c', 'command');
    } else {
      robot.keyTap('c', 'control');
    }

    setTimeout(() => {
      //Read clipboard contents
      const text = clipboard.readText('clipboard') || ''
      const fileUrl = clipboard.read('public.file-url');
      
      //Restore clipboard contents
      clipboard.writeText(lastText);

      resolve({
        text,
        fileUrl
      })
    }, 300);
  })
}

Notifies the super panel window of the current selection

After the selected content is obtained, the next step is to create a super panelBrowserWindow:

const { BrowserWindow, ipcMain, app } = require("electron");

module.exports = () => {
  let win;

  let init = (mainWindow) => {
    if (win === null || win === undefined) {
      createWindow();
    }
  };

  let createWindow = () => {
    win = new BrowserWindow({
      frame: false,
      autoHideMenuBar: true,
      width: 250,
      height: 50,
      show: false,
      alwaysOnTop: true,
      webPreferences: {
        webSecurity: false,
        enableRemoteModule: true,
        backgroundThrottling: false,
        nodeIntegration: true,
        devTools: false,
      },
    });
    win.loadURL(`file://${__static}/plugins/superPanel/index.html`);
    win.once('ready-to-show', () => win.show());
    win.on("closed", () => {
      win = undefined;
    });
  };

  let getWindow = () => win;

  return {
    init: init,
    getWindow: getWindow,
  };
};

Then notifysuperPanelContent display:

 win.webContents.send('trigger-super-panel', {
  ...copyResult,
  optionPlugin: optionPlugin.plugins,
});

Super panel click operation

Next, to realize the click operation of the super panel, it is also relatively simple. Just go to the code directly:

1. Open terminal

const { spawn } = require ('child_process');

spawn('open', [ '-a', 'Terminal', fileUrl ]);

2. Create a new file

remote.dialog.showSaveDialog({
  Title: "please select the file name to save",
  Buttonlabel: "save",
  defaultPath: fileUrl.replace('file://', ''),
  showsTagField: false,
  nameFieldLabel: '',
}).then(result => {
  fs.writeFileSync(result.filePath, '');
});

3. Copy path

clipboard.writeText(fileUrl.replace('file://', ''))

last

This article mainly introduces how to implement a super panel function similar to utools. Of course, this is far from the whole of utools. In the next issue, we will continue to introduce how to implement other capabilities of utools. Welcome to experienceRubickAny questions can be raised at any time, and we will give feedback in time.

In addition, if you think the design and implementation ideas are useful to you, you are also welcome to give a star:https://github.com/clouDr-f2e/rubick