Skip to content
This repository has been archived by the owner on May 21, 2019. It is now read-only.

Enable Multi-Window Support #923

Closed
wants to merge 6 commits into from
Closed

Enable Multi-Window Support #923

wants to merge 6 commits into from

Conversation

jsphpl
Copy link
Contributor

@jsphpl jsphpl commented Dec 7, 2016

Changes and refactoring applied to achieve this:

  • Store window instances in windows global in main/Main.ts
  • Introduce methods for window handling including focussed window in main/Main.ts
  • Add menu items and keybindings for new/close window
  • Make the electronWindow a property of the ApplicationComponent so we can .close() it when all tabs are gone
  • Move Menu.ts to src/main/ and register it from within the main process, don't bind the handlers to specifit windows
  • Create handlers in ApplicationComponent for all menu events so we can call them on the currently focussed window
  • Move Keybindings.ts to src/

- Allow multiple windows (fixes #922)
- Keep application running on osx after all windows have been closed (fixes #365)

Changes and refactoring applied to achieve this:
- Store window instances in `windows` global in `main/Main.ts`
- Introduce methods for window handling including focussed window in `main/Main.ts`
- Add menu items and keybindings for new/close window
- Make the `electronWindow` a property of the `ApplicationComponent` so we can `.close()` it when all tabs are gone
- Move `Menu.ts` to `src/main/` and register it from within the main process, don't bind the handlers to specifit windows
- Create handlers in `ApplicationComponent` for all menu events so we can call them on the currently focussed window
- Move `Keybindings.ts` to `src/`
@jsphpl
Copy link
Contributor Author

jsphpl commented Dec 7, 2016

Sorry guys, this has gotten larger than expected.

Had to refactor the menu item handlers to emit events on the passed-in BrowserWindow so the commands are directed to the currently focussed window. Hope this won't break with any other changes currently in the queue…

What do you think, @pmkary, @drew-gross & @vshatskyi ?

Copy link
Contributor

@drew-gross drew-gross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks awesome! A couple minor nits, plus one larger change thats optional.

I'll give @vshatskyi some time to take a look too, if he wants.

src/main/Main.ts Outdated
createWindow();
registerApplicationMenu();

globalShortcut.register("CommandOrControl+N", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can shorten this to just globalShortcut.register("CommandOrControl+N", createWindow)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

src/main/Main.ts Outdated
*
* @param {Electron.BrowserWindow} browserWindow
*/
function setFocssedWindow(browserWindow: Electron.BrowserWindow) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Focssed/Focussed/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed.

*
* @type {Array}
*/
let windows: Electron.BrowserWindow[] = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it might be possible to have a Set of unfocussedWindows and a single, optional, focussedWindow, with windows moving into and out of the focussedWindow? That feels like it might be a little cleaner than an array with an index. I think it would probably also be fine as is if you don't have the time to change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw the tabs are stored the same way, just in an array with an extra variable containing the index of the currently focussed one. Would be probably clean to keep consistent with the rest of the project, what do you think?

Copy link

@pouyakary pouyakary Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsphpl I agree with @drew-gross so much. Having sets for the job makes it hell lot easier to manage the communications in windows. And yeah I agree that would be cleaner design to have the property storing the focused one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could update tabs to use the Setstrategy too :p up to you. I don't mind too much either way.

Copy link
Contributor

@vlad-shatskyi vlad-shatskyi Dec 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This data structure is called a zipper: a "traversable" with focus. We can create a polymorphic one and use it everywhere. An array plus an index is a legit array zipper, but we could make a nice interface for it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vshatskyi I also need zipper for my apps. So I'm on it. Let me make a full bullet-proof object and then I'll make a PR for that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pmkary, then maybe you'd be interested in a way to restrict the domain of the activate function to only accept existing elements. https://github.com/vshatskyi/black-screen/blob/3e24644dd8ec5a16f240e05291437000497588a7/src/shell/Aliases.ts#L44-L50

Copy link

@pouyakary pouyakary Dec 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vshatskyi so I think this will do it, It also has an event system for the change of the focus

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what should i do? I'd like to avoid the initial suggestion from @drew-gross (sorry) because it's an additional shifting around of data, especially given that it's gonna be replaced by a zipper. In order to achieve a similar outcome for the meantime, we could just introduce a getFocussedWindow() function for ease of access, what do you think?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsphpl did you look at this zipper implementation? I think this will ease your work so much. I have taken care of all the zipper stuff that could come to my mind.

/**
* The index of the currently focussed window in `windows`.
*
* @type {number}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why you need this additional type annotation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is usually used for programmatically generating documentation. In case you definitely don't want to do this, i can remove them…

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind, it's just this information already exists as types.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsphpl typescript is aware of the params so jsdoc is no longer needed... You can just explain what the parameter does. But you don't need to specify any types on the documentations...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pmkary is there a typescript pendant of jsdoc that makes use of that fact?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsphpl Most of the codes out there simply use plain /** ... */ comments. But you can have @param and stuff too so like:

screen shot 2016-12-11 at 4 37 18 pm

But it's for just when you really need to specify the each param so you can have hover options. Most of the times you just go with a simple description of the function and because TypeScript is aware of the types it generates you a perfectly well explained scheme of the function:

screen shot 2016-12-11 at 4 43 07 pm

Also you can have markdown:

screen shot 2016-12-11 at 4 50 07 pm

So you can just bold or italic the params and type faster and more clear

);
ipcRenderer
// Tabs actions
.on("change-working-directory", (_event: Electron.IpcRendererEvent, directory: string) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to connect events in a move static way so that the compiler could verify our callbacks have the proper signatures and that such events exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Shall i introduce constants for each in Enums?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally I'd like to have direct function calls instead of events, but I'm not sure it's possible. An enum would be nice too. Alternatively you can extend the interface of the on function to accept particular strings. This way you can verify the second argument based on the first one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about a class with two abstract methods send() and receive(). Each "message channel" (e.g. change-working-directory) would be a subclass, where the programmer's task would be to make sure both methods have the same signature.

In the "emitting" process, one would call send(), which has the sole purpose of serializing its arguments using private methods and emit the ipc message. The "listening" process would subscribe to the channel by calling a method for that purpose on our class, causing our receive() method to be executed on the receipt of the message. The receive() call would be made internally by our class, which would take care of subscribing to the channel and deserializing the arguments.

Does that make any sense and would that even be helpful?

One problem i see here is that we'd have to bring other variables into the scope of the receive() method…

Here how the implementation for our "change-working-directory" example could look like:

class ChangeWorkingDirectory extends RemoteCall {
	/**
	 * Only there to call `serializeAndEmit` with all its arguments.
	 */
	send(directory: string) {  // same signature
		this.serializeAndEmit({directory: directory});
	}
	/**
	 * Implement the functionality here.
	 */
	receive(directory: string) {  // same signature
		this.context.focusedTab.focusedPane.session.directory = directory;
		// context could be passed in on event subscription
	}
}

@vlad-shatskyi
Copy link
Contributor

@jsphpl I plan to close old PRs to keeps things clean. Is it OK with you if I close this one?

@jsphpl
Copy link
Contributor Author

jsphpl commented May 30, 2017

@vshatskyi yes, please close it. Looks like this has fallen asleep and needs a new attempt off the new codebase. Has there been any progress on multi window support in the mean time?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multi-Window support? Application quits when window closed on OSX
4 participants