I would like to change handling of key pressed events for example to do something else when user press up/down arrows but when i add eventHandler by setOnKeyPressed/setOnKeyReleased i cant stop native handling of those keys.
Example:
treeView.setOnKeyPressed(new EventHandler<KeyEvent>() {
private KeyCodeCombination prevNodeKeyCombination = new KeyCodeCombination(KeyCode.UP);
private KeyCodeCombination nextNodeKeyCombination = new KeyCodeCombination(KeyCode.DOWN);
public void handle(KeyEvent event)
{
if (prevNodeKeyCombination.match(event))
{
selectPrevSibling();
}
else if (nextNodeKeyCombination.match(event))
{
selectNextSibling();
}
event.consume(); // i try to block anything that goes by
}
});
Any idea how to override native key handling, if necessary i can try extend TreeView if that helps in something?
To override native handling of event use addEventFilter in my case it was:
treeView.addEventFilter(KeyEvent.ANY, new EventHandler<KeyEvent>() { ... }
Related
This question already has answers here:
How to detect click/touch events on UI and GameObjects
(4 answers)
Closed 3 years ago.
I have a UI button. I want to show a text when the user is pressing the button and hide the text when the user releases the button.
How can I do this?
this anwser is basically fine but there is a huge drawback: You can't have additional fields in the Inspector since Button already has a built-in EditorScript which overwrites the default Inspector - You would need to extend this via a custom Inspector every time.
I would instead implement it as completely additional component implementing IPointerDownHandler and IPointerUpHandler (and maybe also IPointerExitHandler to reset also when exiting the button while holding the mouse/pointer still pressed).
For the doing something while the button stays pressed I'ld use a Coroutine.
In general I'ld use UnityEvents:
[RequireComponent(typeof(Button))]
public class PointerDownUpHandler : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
{
public UnityEvent onPointerDown;
public UnityEvent onPointerUp;
// gets invoked every frame while pointer is down
public UnityEvent whilePointerPressed;
private Button _button;
private void Awake()
{
_button = GetComponent<Button>();
}
private IEnumerator WhilePressed()
{
// this looks strange but is okey in a Coroutine
// as long as you yield somewhere
while(true)
{
whilePointerPressed?.Invoke();
yield return null;
}
}
public void OnPointerDown(PointerEventData eventData)
{
// ignore if button not interactable
if(!_button.interactable) return;
// just to be sure kill all current routines
// (although there should be none)
StopAllCoroutines();
StartCoroutine(WhilePressed);
onPointerDown?.Invoke();
}
public void OnPointerUp(PointerEventData eventData)
{
StopAllCoroutines();
onPointerUp?.Invoke();
}
public void OnPointerExit(PointerEventData eventData)
{
StopAllCoroutines();
onPointerUp?.Invoke();
}
// Afaik needed so Pointer exit works .. doing nothing further
public void OnPointerEnter(PointerEventData eventData) { }
}
Than you can reference any callback in onPointerDown, onPointerUp and whilePointerPressed just the way you would do it with the onClick event of the Button.
You have to create your own custom button by extending Button class and override method OnPoiterDown and OnPointerUp.
Attach MyButton component instead of Button to your gameobject
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class MyButton : Button
{
public override void OnPointerDown(PointerEventData eventData)
{
base.OnPointerDown(eventData);
Debug.Log("Down");
//show text
}
public override void OnPointerUp(PointerEventData eventData)
{
base.OnPointerUp(eventData);
Debug.Log("Up");
//hide text
}
}
I've started working with the new navigation component and I'm really digging it! I do have one issue though - How am I supposed to handle the back button when I'm at the starting destination of the graph?
This is the code I'm using now:
findNavController(this, R.id.my_nav_host_fragment)
.navigateUp()
When I'm anywhere on my graph, it's working great, it send me back, but when I'm at the start of it - the app crashes since the backstack is empty.
This all makes sense to me, I'm just not sure how to handle it.
While I can check if the current fragment's ID is the same as the one that I know to be the root of the graph, I'm looking for a more elegant solution like some bool flag of wether or not the current location in the graph is the starting location or not.
Ideas?
I had a similar scenario where I wanted to finish the activity when I was at the start destination and do a regular 'navigateUp' when I was further down the navigation graph. I solved this through a simple extension function:
fun NavController.navigateUpOrFinish(activity: AppCompatActivity): Boolean {
return if (navigateUp()) {
true
} else {
activity.finish()
true
}
}
And then call it like:
override fun onSupportNavigateUp() =
findNavController(R.id.nav_fragment).navigateUpOrFinish(this)
However I was unable to use NavigationUI as this would hide the back arrow whenever I was at the start destination. So instead of:
NavigationUI.setupActionBarWithNavController(this, controller)
I manually controlled the home icon:
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_navigation_back)
Override onBackPressed in your activity and check if the current destination is the start destination or not.
Practically it looks like this:
#Override
public void onBackPressed() {
if (Navigation.findNavController(this,R.id.nav_host_fragment)
.getCurrentDestination().getId() == R.id.your_start_destination) {
// handle back button the way you want here
return;
}
super.onBackPressed();
}
You shouldn't override "onBackPressed", you should override "onSupportNavigateUp" and put there
findNavController(this, R.id.my_nav_host_fragment)
.navigateUp()
From the official documentation:
You will also overwrite AppCompatActivity.onSupportNavigateUp() and call NavController.navigateUp
https://developer.android.com/topic/libraries/architecture/navigation/navigation-implementing
In Jetpack Navigation Component, if you want to perform some operation when fragment is poped then you need to override following functions.
Add OnBackPressedCallback in fragment to run your special operation when back present in system navigation bar at bottom is pressed .
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
//perform your operation and call navigateUp
findNavController().navigateUp()
}
}
requireActivity().onBackPressedDispatcher.addCallback(onBackPressedCallback)
}
Add onOptionsItemMenu in fragment to handle back arrow press present at top left corner within the app.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
//perform your operation and call navigateUp
findNavController().navigateUp()
return true
}
return super.onOptionsItemSelected(item)
}
If there is no special code to be run when back is pressed on host fragment then use onSupportNavigateUp in Activity.
override fun onSupportNavigateUp(): Boolean {
if (navController.navigateUp() == false){
//navigateUp() returns false if there are no more fragments to pop
onBackPressed()
}
return navController.navigateUp()
}
Note that onSupportNavigateUp() is not called if the fragment contains onOptionsItemSelected()
As my back button works correctly, and using NavController.navigateUp() crashed on start destination back button. I have changed this code to something like this. Other possibility will be to just check if currentDestination == startDestination.id but I want to close Activity and go back to other Activity.
override fun onSupportNavigateUp() : Boolean {
//return findNavController(R.id.wizard_nav_host_fragment).navigateUp()
onBackPressed()
return true
}
/** in your activity **/
private boolean doubleBackToExitPressedOnce = false;
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
public void onBackPressed() {
int start = Navigation.findNavController(this, R.id.nav_host_fragment).getCurrentDestination().getId();
if (start == R.id.nav_home) {
if (doubleBackToExitPressedOnce) {
super.onBackPressed();
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(MainActivity.this, "Press back again to exits", Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
doubleBackToExitPressedOnce = false;
}
}, 2000);
} else {
super.onBackPressed();
}
}
If you mean the start of your "root" navigation graph (just incase you have nested navigation graphs) then you shouldn't be showing an up button at all, at least according to the navigation principles.
Just call this in your back button Onclick
requireActivity().finish()
I have a RichTextArea
private RichTextArea richTextArea;
and I'm trying to capture a paste event like this:
DOM.sinkEvents((com.google.gwt.user.client.Element) richTextArea.getElement(), com.google.gwt.user.client.Event.ONPASTE);
DOM.setEventListener((com.google.gwt.user.client.Element) richTextArea.getElement(), new EventListener(){
#Override public void onBrowserEvent(Event event) {
switch (event.getTypeInt()) {
case Event.ONPASTE: Window.alert("hey");break;
}
}
});
But it doesn't work, when I paste text on the richTextArea the alert is not triggered.
Any idea how to capture this paste event?
Thanks!
You cannot add the event to the RichTextArea, which actually is an iframe, but to it's body.
Although you could use jsni, I would use gwtquery because its simplicity:
// First attach the widget to the DOM
RootPanel.get().add(richTextArea);
// We only can bind events to the content, once the iframe document has been created,
// and this happens after it has been attached. Note that richtTextArea uses a timeout
// to initialize, so we have to delay the event binding as well
$(richTextArea).delay(1, lazy().contents().find("body").bind(Event.ONPASTE, new Function(){
#Override public boolean f(Event e) {
Window.alert("OnPaste");
return true;
}
}).done());
Have you seen the rendered HTML of RichTextArea ? it's an iframe not an actual textarea input type. it sets the user input under a body element. So that's why you don't get sinked onpaste events. For example if you listen to onpaste on TextArea widget it works fine.
private static class MyTextArea extends TextArea
{
public MyTextArea()
{
sinkEvents(Event.ONPASTE);
}
#Override
public void onBrowserEvent(Event event)
{
if(event.getTypeInt() == Event.ONPASTE)
{
Window.alert("text pasted !");
}
super.onBrowserEvent(event);
}
}
maybe you can bind a handler to that iframe body element using JSNI and get the callback on that event (haven't tried it though )
Just for the sake of completeness, the native (JSNI) solution would be something like:
setPastehandler(richTextArea.getElement());
private native void setPasteHandler(Element e) /*-{
e.contentDocument.onpaste = function (event) {
alert("pasted!");
};
}-*/;
I am setting selecteditem manually
public pageXXXX()
{
InitializeComponent();
this.cargaLista();
}
private void cargaLista()
{
this.lPickTipo.SelectedItem = this.lPickTipo.Items.OfType<tipos>().First(i => i.tipo == varString);
// here i load other data
//
}
Ok. Runs fine.
But my problem is that selectionchanged event is fire last, not when I set manually the SelectedItem
This is a problem for me. Because I run calc inside "SelectionChanged" event and I need to run calc when I selecteditem because other functions depend on this result
private void lPickTipo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
if (this.lPickTipo.SelectedItem != null)
{
if (lPickTipo.SelectedIndex > -1)
{
this.calcularTotales();
}
}
}
catch (Exception EXC)
{ // CACTHING }
}
Why is fire last? How I can solve this?
In that you can't change the order that system level events are raised you'll need to change your logic to account for what the platform does.
As you haven't provided any information on what you're actually basing on the selection or why it requires page (presumably) level events to be fired after the selection is changed it's hard to be more specific.
Im am making an Java SWT program that is required to run on both Linux and Windows.
I use the following Code to listen for KeyUp events:
Control.addListener(SWT.KeyUp, new Listener() {
public void handleEvent(Event arg0) {
System.out.println("Event");
}
});
But this does not trigger when no control has focus.
Do anyone know of a place i can add a listener that acts as a Catch-all?
The only way of doing this that I'm aware of is by placing a filter on the Display. Take note that multiple Shells may operate on one Display, so you should be careful!
shell.getDisplay().addFilter(SWT.KeyDown, new Listener() {
public void handleEvent(final Event event) {
System.out.println(event);
}
});
try following method in Display class:
public void addListener ( int eventType, Listener listener )
I have not been able to find a solution to this. I suspect none exist