Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contextmenu #444

Closed
wants to merge 8 commits into from
Closed

Contextmenu #444

wants to merge 8 commits into from

Conversation

rowild
Copy link
Contributor

@rowild rowild commented Mar 3, 2022

Add contextmenu to waveforms and segment-shape.

@rowild rowild marked this pull request as draft March 3, 2022 19:44
@rowild
Copy link
Contributor Author

rowild commented Mar 3, 2022

Hi, Chris!
I am still trying to implement contextmenu, for the sake of "feature completeness" I thought it might be good to add this functionality to the waveforms and the segment shapes, too.
Problems arise, when contextmenu is used on pointer AND on the zoomview. Then, both implementations get fired. And for some reason the click event is also still recognised.

I was looking at eventEmitter3 (I think this is what peaks uses, right?) and if it offers any cancelation settings (stop bubbling). But so far no good...

The video shows, what happens in the console.
Any ideas?

peaks-contextmenu-events-overlapping.mp4

PS: I should add a code example right here:

// Index.html

peaksInstance.on('segments.contextmenu', function(segment) {
  event.stopPropagation();
  event.preventDefault();
  console.log('segments.contextmenu:', segment);
});

PPS: Konva provides a cancelBubble option: https://konvajs.org/docs/events/Cancel_Propagation.html
Not sure how to implement this...

@chrisn
Copy link
Member

chrisn commented Mar 5, 2022

Problems arise, when contextmenu is used on pointer AND on the zoomview. Then, both implementations get fired.

The pointer and zoomview handlers are independent of each other, so I'm not sure yet how to make one have precedence over the other.

And for some reason the click event is also still recognised.

We could work around this by using the click event button attribute:

peaks.on("zoomview.contextmenu", (event) => {
  event.evt.preventDefault();
});

peaks.on("zoomview.click", (event) => {
  if (event.evt.button === 2) {
    // Right click
  }
  else {
    // Left click
  }
});

I was looking at eventEmitter3 (I think this is what peaks uses, right?) and if it offers any cancelation settings (stop bubbling).

Yes, we use EventEmitter3. You're right, it doesn't provide any cancellation settings.

@rowild
Copy link
Contributor Author

rowild commented Mar 5, 2022

Where would I have to add this code snippet? In "index.html" (examples)event.evt (...) does not work. The .evt. seems to be too much...

UPDATE: Ah, I see. That would be an addition to the core scripts, in the given case waveform-zoomview, around line 238?

If both, points.contextmenu and zoomview.contextmenu are defined, and a button detection would be integrated, than still both contextmenus would get fired, no matter how many cancelBubbles and bubbles and propagations are set to false...:

// index.html

  peaksInstance.on('points.contextmenu', function(point) {
    event.preventDefault();
    event.stopPropagation();
    event.stopImmediatePropagation();

    // // read-only, changing them does nothing
    // event.cancelBubble = true
    // event.bubbles = false

    if(event.button === 2) {
      console.log('points: right click detected');
    }
  });

  peaksInstance.on('zoomview.contextmenu', function(time) {
    event.stopPropagation();
    event.preventDefault();
    event.stopImmediatePropagation();

    if(event.button === 2) {
      console.log('zoomview: right click  detected');
    }
  });

@rowild
Copy link
Contributor Author

rowild commented Mar 5, 2022

My thinking is wrong: whether the contextmenu is already active or not should not be something that peaks should handle.
Assuming that a marker will always be on top of the waveform, the points.contextmenu could simply set a variable, e.g.cmActive = true. The zoomview in turn would check, whether that variable is true or not and react accordingly:

// index.html

let cmActive = false

peaksInstance.on('points.contextmenu', point => {
  if(event.button === 2) {
    this.cmActive = true
    // call the right-click drop-down menu
  }
});

peaksInstance.on('zoomview.contextmenu', time => {
  if(event.button === 2) {
    if(!this.cmActive) {
      // Do something!
    } else {
      // Do nothing!
    }
  }
});

In my scenario this solution works fine. What do you think?

@rowild
Copy link
Contributor Author

rowild commented Mar 11, 2022 via email

@chrisn
Copy link
Member

chrisn commented Mar 11, 2022

Sorry for the delay...

I should try to clarify my previous reply. With this code I'm suggesting that we should expose an event object rather than just the time position (as we do for click events), so you could do:

peaks.on("zoomview.contextmenu", (event) => {
  event.evt.preventDefault(); // event.evt exposes the DOM event
  console.log(event.time);
});

This is simiar to how Konva events work.

I notice your code uses window.event which I think we should avoid if we can (see MDN docs) - but I understand it's the only option right now.

Then, for consistency probably all events should have a similar interface (see #427). I haven't merged this yet as it's a breaking API change. I'm interested in your feedback on this idea.

For contextmenu specifically, an issue I've found is that when you right-click you get both events:

  • zoomview.click
  • zoomview.contextmenu

So I'm thinking that contextmenu would only be used to prevent the browser default behaviour and applications would then use the points.click or zoomview.click events and check event.evt.button as in:

peaks.on("zoomview.click", (event) => {
  if (event.evt.button === 2) {
    // Right click
    console.log(event.time); // Time position
  }
  else {
    // Left click
    console.log(event.time); // Time position
  }
});

You've changed your mind on whether Peaks should suppress zoomview.contextmenu if the user clicks on a point. I think it might make sense for Peaks to handle this itself, but happy to leave that to applications for now and revisit if we need to.

@rowild
Copy link
Contributor Author

rowild commented Mar 11, 2022 via email

@chrisn chrisn marked this pull request as ready for review March 12, 2022 14:20
@rowild
Copy link
Contributor Author

rowild commented Mar 12, 2022

My lack of experience of working with git in a team now shows itself. Usually, when a PR is merged, github tells me that I can delete m branch. But this is not the case here. So even though I think that you merged your and my scripts, I am not sure if I can delete my branch.

The reason I ask is, because I need to implement this lib on Vercel. They do support private repos, but only from the master branch, if I am not mistaken. And my master branch is "2 commits ahead". I must have something committed sth on the master branch (which is a no-go on forks, I know).

Any hint what my next steps should be? What would happen, if I deleted my fork now? would this mess up some of your current work on peaks?

@chrisn
Copy link
Member

chrisn commented Mar 12, 2022

I haven't merged this PR yet. GitHub says there are some conflicts to resolve. I'll fix those and merge, then publish a new release from the master branch.

@rowild
Copy link
Contributor Author

rowild commented Mar 12, 2022

I cloned peaks, installed dependencies, build it, then npm link, and in my project folder npm link peaks.js. Unfortunately the changed events do not work.

When I add

/**
* Event on a point: click, dblclick, enter,
*/
this.peaks.on('points.contextmenu', function (event) {
    event.evt.preventDefault();

    console.log('points.contextmenu:', event);
});

nothing happens. Same thing for my updated scripts.

// Old
this.peaks.on('points.dblclick', point => {
  console.log('points.dblclick point =', point);
  if (point.id !== 'peaks.point.1' && point.id !== 'peaks.point.2') {
    $nuxt.$emit('showModalForDescriptors', point)
  }
})

// New
this.peaks.on('points.dblclick', event => {
  console.log('points.dblclick event =', event);
  if (event.point.id !== 'peaks.point.1' && event.point.id !== 'peaks.point.2') {
    $nuxt.$emit('showModalForDescriptors', event.point)
  }
})

@rowild
Copy link
Contributor Author

rowild commented Mar 12, 2022

Oh... overlap! Sorry!

@chrisn
Copy link
Member

chrisn commented Mar 12, 2022

This is now merged. Thank you for contributing! 👍

@chrisn chrisn closed this Mar 12, 2022
@rowild rowild deleted the contextmenu branch March 12, 2022 19:34
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.

2 participants