Skip to content

Added hover UI for front end#228

Merged
cmyr merged 19 commits intoxi-editor:masterfrom
nangtrongvuon:hover-frontend
Jul 26, 2018
Merged

Added hover UI for front end#228
cmyr merged 19 commits intoxi-editor:masterfrom
nangtrongvuon:hover-frontend

Conversation

@nangtrongvuon
Copy link
Copy Markdown
Member

@nangtrongvuon nangtrongvuon commented Jul 18, 2018

This is the hover part of #221. This PR adds the hover UI to Xi to work with the LSP's hover feature.

In order to use hover, Option+Click on the desired symbol.
This PR can be tested using my dummy plugin, or with xi-editor's #753.

This commit adds support for the `show_hover` notification that comes
from core.
Hover results now display in a popover placed at the baseline of the
selected symbol.
EditView will now update its tracking area for mouseMoved, to be used
with the hover event.
The height calculation will now take in account the fixed width of 500 for the hover view.
Clarify that 500 was chosen because it is XCode's width for quick help.
@cmyr
Copy link
Copy Markdown
Member

cmyr commented Jul 18, 2018

Okay playing around this seems excellent! The only visual issue I see is that a second option+click while the hover view is visible causes a jerky animation.

@betterclever
Copy link
Copy Markdown
Member

I was testing this out, noticed a new notable bugs/ unintended behaviour.

  • The View is rendered a bit-off sometimes.
  • If you open a Hover and then scroll, the hover view keeps anchored to the original position. The ideal behaviour should be that it should dismiss or keep anchored to the textual position (not highly preferred)

I am not sure if it is happening with only me or is a general problem. I am using a multi-monitor setup so not sure if that is causing any error (since I read you discussing some issue about it on IRC) though I noticed it with a single screen plugged in as well.

@nangtrongvuon
Copy link
Copy Markdown
Member Author

@betterclever Heya, can you explain a little bit more on how the view is rendered a bit off sometimes? As in, does the arrow point to weird places?

Gotcha on the popover behaviour, I'll try to tweak it and find out what works best.

@betterclever
Copy link
Copy Markdown
Member

It is a but hard to describe it here. Probably it is best to do a screenshare where I can show you what is happening in my computer.

Copy link
Copy Markdown
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

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

Some observations. There are also two rendering issues, one of which is a bug and one of which is maybe more of a design thing, but both of which are present in this screenshot:

screen shot 2018-07-20 at 12 09 45 pm

The bug is that the vertical position of the hover view does not take the scroll position into account. (My cursor is on line 16)

The design thing is that I think the hover view should shrink horizontally (with a minimum size) if the text its displaying does not fill the view.

Otherwise this is looking good!

Comment thread XiEditor/EditViewController.swift Outdated
var hoverRequestID = 0

override func viewDidLoad() {
if let path = ProcessInfo.processInfo.environment["PATH"] {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we probably don't need to commit this.

Comment thread XiEditor/EditViewController.swift Outdated
}


override func mouseMoved(with theEvent: NSEvent) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this supposed to do anything? I can't seem to get it to work.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is the one part that I don't think this implementation would do justice. In order to get us a real hover, some more work that hooks on text is required. For now, Option+Click is the most reliable option when trying to get a hover to work.

Comment thread XiEditor/EditViewController.swift Outdated
let hoverPosition = editView.bufferPositionFromPoint(event.locationInWindow)
hoverTimer?.invalidate()
hoverTimer = nil
document.sendRpcAsync("request_hover", params: ["request_id": hoverRequestID, "position": ["type": "utf8_line_char", "line": hoverPosition.line, "character": hoverPosition.column]])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we're going to use this in two places, I would add a method to Document that handles the implementation.

I believe that the RPC expected in core has changed as well; we don't send type and we send column instead of character.

Comment thread XiEditor/EditViewController.swift Outdated
if gestureType == "request_hover" {
document.sendRpcAsync("request_hover", params: ["request_id": hoverRequestID, "position": ["type": "utf8_line_char", "line": position.line, "character": position.column]])
hoverEvent = theEvent
hoverRequestID += 1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we don't seem to actually ever use this anywhere. Perhaps it's unnecessary?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It is used for when an Option+Click is detected.

// Lets AppKit do all the work when setting up a new text view
override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
let textView = NSTextView(frame: .zero)
super.init(frame: frameRect, textContainer: textView.textContainer)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this override doesn't seem to actually do anything. What happens if we just leave it out?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

init(frame, container) is the designated initializer for NSTextView subclasses. I had to let Cocoa make a text container on its own and use that container, which also contains conveniently created a NSLayoutManager and a NSTextStorage for my own NSTextView, since otherwise I would have had to create my own collection of objects associated with my custom text view.

}()
var resultContent: String
var hoverView: HoverView
let hoverPopoverWidth: CGFloat = 500 // XCode size for quick help popovers
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should probably have a minimum and a maximum width, and then we resize ourself to the preferred width of our text, within those constraints.

Copy link
Copy Markdown
Member Author

@nangtrongvuon nangtrongvuon Jul 21, 2018

Choose a reason for hiding this comment

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

I tried this, and there is a whole host of unintended behaviors when I tried to get the view to size itself to fit all the text perfectly.

Through much trial and error I and lots of reading I realized many macOS popovers (not just in XCode) works best when they have a fixed width or fixed height, because it's much easier to calculate how much space text would take in one direction. Fixed width was chosen because scrolling vertically is much more intuitive than scrolling horizontally.

Also, I think it looks a bit more aesthetically more pleasing this way. :)

Popovers now take in account the current scroll origin, which would fix
popovers showing up at the wrong places.
This commit also justifies the text which is in the hover's content.
@nangtrongvuon nangtrongvuon force-pushed the hover-frontend branch 2 times, most recently from 9c1bf6c to 1107f7c Compare July 21, 2018 16:23
Comment thread XiEditor/EditViewController.swift Outdated
@objc func sendHover() {
if let event = hoverEvent {
let hoverPosition = editView.bufferPositionFromPoint(event.locationInWindow)
hoverTimer?.invalidate()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Compilation is failing for me on these two lines. Looking more closely, I see it is not declared anywhere.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also, after removing these lines, hover does not work. Am I missing something?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No, this was my fault, I hastily committed these. It should be working now.

The hover needs more work before it can be implemented. The state that
is in the PR right now is not sufficient.
This commit fixes an issue that can be see on white backgrounds, where the scroll view kept the popover's background, causing it to show up and making the popover corners not actually look rounded.
This will make it clearer that holding Option will enter an "inspection" state, and clicking on valid symbols will trigger a hover.
This commit removes background off the popover's content views, so it uses the natural popover background, maintaining consistency with other macOS apps.
Copy link
Copy Markdown
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

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

Okay, one more little thing and I think this is good to go.

infoPopover.contentSize = hoverContentSize
infoPopover.contentViewController = hoverViewController

if let event = hoverEvent {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

along with checking for hoverEvent, we should also make sure that the request_id we get back is the same is current. Otherwise we can have a situation where we request hover multiple times, and then the first result arrives and is displayed even though it isn't what we want.

The scroll view will now only expand in height up to the predefined popover width, to prevent cases where an overly long comment will cause a very tall popover to show.
Comment thread XiEditor/HoverViewController.swift Outdated
let positioningSize = CGSize(width: 1, height: 1) // Generic size to center popover on cursor

infoPopover.show(relativeTo: NSRect(origin: positioningPoint, size: positioningSize), of: self.view, preferredEdge: .minY)
hoverRequestID += 1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doesn't this mean multiple requests could be sent with the same ID?

Copy link
Copy Markdown
Member Author

@nangtrongvuon nangtrongvuon Jul 24, 2018

Choose a reason for hiding this comment

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

Yes, but I'm not quite sure what the request ID is meant to represent in the first place. As it is right now, the ID doesn't get incremented if a result wasn't shown, which happens when:

  1. The result we get back is empty (as in, fallback errors and the like)
  2. The request ID in the RPC notification we get from core is different from what's currently expected

Since (in my personal opinion) I think that hover requests should return results instantly, it's fine even if we send many requests with the same ID?

Now request IDs are checked to make sure the hover results shown are not
stale.
Having the popover animate felt jarring when quickly switching between
different symbols, and as such animations were disabled. This also gives
the advantage and illusion of a smoother experience.
Copy link
Copy Markdown
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

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

Okay this is working well for me. Will hold the merge until we get the core work in, but lets freeze this and focus on auto-complete. 👍

Comment thread XiEditor/HoverViewController.swift Outdated
infoPopover.performClose(self)
}

let hoverContent = result["content"] as! String
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As per latest work in xi-editor/xi-editor#753 , the result is now just a String, so you don't need to extract content out here.

show_hover has been changed to handle a String instead of a Dictionary.
Copy link
Copy Markdown
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

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

👍

@cmyr cmyr merged commit ff95951 into xi-editor:master Jul 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants