I have a Dialog with few buttons. When I disable the dialog, and I click on a child button, nothing happens. But as soon as I enable the Dialog again, the mouse event for the button is handled.
Does disabling a Dialog simply delays handling of any mouse events for its child widgets until enabled again?
I tried installing event filters for child widgets when dialog is disabled and then removing the event filters when enabled again. But it provides the same behavior. As soon as dialog is enabled i.e. event filters for child widgets removed, the mouse event (when disabled) are handled.
Can someone please help whats wrong here?
void MyDialog::changeEvent(QEvent *e)
{
if(e->type() == QEvent::EnabledChange)
{
if(isEnabled())
{
qDebug("Filters removed!\n");
ui->pbtnOption1->removeEventFilter(this);
ui->pbtnOption2->removeEventFilter(this);
ui->pbtnOption3->removeEventFilter(this);
}
else
{
qDebug("Filters installed\n");
ui->pbtnOption1->installEventFilter(this);
ui->pbtnOption2->installEventFilter(this);
ui->pbtnOption3->installEventFilter(this);
}
}
}
bool MyDialog::eventFilter(QObject *obj, QEvent *e)
{
if(obj == ui->pbtnOption1 || obj == ui->pbtnOption2 || obj == ui->pbtnOption3)
{
if(e->type() == QEvent::MouseButtonPress)
{
qDebug("MousePress Event!\n");
return true;
}
else
return false;
}
return QDialog::eventFilter(obj, e);
}
Related
I have a Prism based Xamarin Forms app that contains an edit page that is wrapped in a Navigation page so there is a back button at top left on both Android and iOS. To avoid the user accidentally losing an edit in progress by accidentally clicking the back button (in particular on Android) we want to prompt them to confirm that they definitely want to cancel.
Thing is, this seems like something that is not baked in to Xamarin forms. You can override OnBackButtonPressed in a navigation page, but that only gets called for the hardware/software back button on Android. There are articles detailing techniques to intercept the actual arrow button at the top left on Android (involving overriding OnOptionsItemSelected in the Android MainActivity), but on iOS I'm not sure it is even possible.
So I can't help but wonder if I am going about this the wrong way? Should I not be intercepting the top left / hardware / software back button in this way? Seems like a pretty common thing to do (e.g. press back when editing a new contact in the android built in Contacts app and you get a prompt) but it really feels like I am fighting the system here somehow.
There are previous questions around this, most relevant appears to be How to intercept Navigation Bar Back Button Clicked in Xamarin Forms? - but I am looking for some broad brush suggestions for an approach here. My objective is to show the user a page with the <- arrow at top left for Android, "Cancel" for iOS but I would like to get some views about the best way to go about it that does not involve me fighting against prism / navigation pages / xamarin forms and (where possible) not breaking the various "best practices" on Android and iOS.
After going down the same path as you and being told not to prevent users from going back, I decided on showing an alert after they tap the back button (within ContentPage.OnDisappearing()) that says something like Would you like to save your work?.
If you go with this approach, be sure to use Application.MainPage.DisplayAlert() instead of just this.DisplayAlert() since your ContentPage might not be visible at that point.
Here is how I currently handle saving work when they click the back button (I consolidated a good bit of code and changed some things):
protected override async void OnDisappearing() {
base.OnDisappearing();
// At this point the page is gone or is disappearing, but all properties are still available
#region Auto-save Check and Execution
/*
* Checks to see if any edits have been made and if a save is not in progress, if both are true, it asks if they want to save, if yes, it checks for validation errors.
* If it finds them, it marks it as such in the model before saving the model to the DB and showing an alert stating what was done
*/
if(!_viewModel.WorkIsEdited || _viewModel.SaveInProgress) { //WorkIsEdited changes if they enter/change data or focus on certain elements such as a Picker
return;
}
if(!await Application.Current.MainPage.DisplayAlert("ALERT", "You have unsaved work! Would you like to save now?", "Yes", "No")) {
return;
}
if(await _viewModel.SaveClaimErrorsOrNotAsync()) { //The return value is whether validation succeeds or not, but it gets saved either way
App.SuccessToastConfig.Message = "Work saved successfully. Try saving it yourself next time!";
UserDialogs.Instance.Toast(App.SuccessToastConfig);
} else if(await Application.Current.MainPage.DisplayAlert("ERROR", "Work saved successfully but errors were detected. Tap the button to go back to your work.", "To Work Entry", "OK")) {
await Task.Delay(200); //BUG: On Android, the alert above could still be displayed when the page below is pushed, which prevents the page from displaying //BUG: On iOS 10+ currently the alerts are not fully removed from the view hierarchy when execution returns (a fix is in the works)
await Application.Current.MainPage.Navigation.PushAsync(new WorkPage(_viewModel.SavedWork));
}
#endregion
}
What you ask for is not possible. The back button tap cannot be canceled on iOS even in native apps. You can do some other tricks like having a custom 'back' button, but in general you shouldn't do that - you should instead have a modal dialog with the Done and Cancel buttons (or something similar).
If you use xamarin forms that code it is work.
CrossPlatform source
public class CoolContentPage : ContentPage
{
public Action CustomBackButtonAction { get; set; }
public static readonly BindableProperty EnableBackButtonOverrideProperty =
BindableProperty.Create(nameof(EnableBackButtonOverride), typeof(bool), typeof(CoolContentPage), false);
public bool EnableBackButtonOverride{
get { return (bool)GetValue(EnableBackButtonOverrideProperty); }
set { SetValue(EnableBackButtonOverrideProperty, value); }
}
}
}
Android source
public override bool OnOptionsItemSelected(IMenuItem item)
{
if (item.ItemId == 16908332)
{
var currentpage = (CoolContentPage)
Xamarin.Forms.Application.
Current.MainPage.Navigation.
NavigationStack.LastOrDefault();
if (currentpage?.CustomBackButtonAction != null)
{
currentpage?.CustomBackButtonAction.Invoke();
return false;
}
return base.OnOptionsItemSelected(item);
}
else
{
return base.OnOptionsItemSelected(item);
}
}
public override void OnBackPressed()
{
var currentpage = (CoolContentPage)
Xamarin.Forms.Application.
Current.MainPage.Navigation.
NavigationStack.LastOrDefault();
if (currentpage?.CustomBackButtonAction != null)
{
currentpage?.CustomBackButtonAction.Invoke();
}
else
{
base.OnBackPressed();
}
}
iOS source
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (((CoolContentPage)Element).EnableBackButtonOverride)
{
SetCustomBackButton();
}
}
private void SetCustomBackButton()
{
var backBtnImage = UIImage.FromBundle("iosbackarrow.png");
backBtnImage = backBtnImage.ImageWithRenderingMode
(UIImageRenderingMode.AlwaysTemplate);
var backBtn = new UIButton(UIButtonType.Custom)
{
HorizontalAlignment =
UIControlContentHorizontalAlignment.Left,
TitleEdgeInsets =
new UIEdgeInsets(11.5f, 15f, 10f, 0f),
ImageEdgeInsets =
new UIEdgeInsets(1f, 8f, 0f, 0f)
};
backBtn.SetTitle("Back", UIControlState.Normal);
backBtn.SetTitleColor(UIColor.White, UIControlState.Normal);
backBtn.SetTitleColor(UIColor.LightGray, UIControlState.Highlighted);
backBtn.Font = UIFont.FromName("HelveticaNeue", (nfloat)17);
backBtn.SetImage(backBtnImage, UIControlState.Normal);
backBtn.SizeToFit();
backBtn.TouchDown += (sender, e) =>
{
// Whatever your custom back button click handling
if(((CoolContentPage)Element)?.
CustomBackButtonAction != null)
{
((CoolContentPage)Element)?.
CustomBackButtonAction.Invoke();
}
};
backBtn.Frame = new CGRect(
0,
0,
UIScreen.MainScreen.Bounds.Width / 4,
NavigationController.NavigationBar.Frame.Height);
var btnContainer = new UIView(
new CGRect(0, 0,
backBtn.Frame.Width, backBtn.Frame.Height));
btnContainer.AddSubview(backBtn);
var fixedSpace =
new UIBarButtonItem(UIBarButtonSystemItem.FixedSpace)
{
Width = -16f
};
var backButtonItem = new UIBarButtonItem("",
UIBarButtonItemStyle.Plain, null)
{
CustomView = backBtn
};
NavigationController.TopViewController.NavigationItem.LeftBarButtonItems = new[] { fixedSpace, backButtonItem };
}
using in xamarin forms
public Page2()
{
InitializeComponent();
if (EnableBackButtonOverride)
{
this.CustomBackButtonAction = async () =>
{
var result = await this.DisplayAlert(null, "Go back?" Yes go back", "Nope");
if (result)
{
await Navigation.PopAsync(true);
}
};
}
}
I have a list view with items that I am allowing to be double clicked and right clicked (to delete the item). Why does control clicking not work on a mac? Thanks in advance.
Edit: My code is
listview.setOnMouseClicked(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
if (event.getButton().equals(MouseButton.PRIMARY))
{
if (event.getClickCount() == 2)
{
System.out.println("Double clicked");
System.out.println("clicked on " + listview.getSelectionModel().getSelectedItem());
}
}
if(event.getButton().equals(MouseButton.SECONDARY))
{
System.out.println("Right click");
}
}
});
My trackpad is set up as secondary button with two finger tap.
For anyone else looking this up. It is correct that two finger clicking will register as a MouseButton.SECONDARY event, but I think you should also check for Ctrl + MouseButton.PRIMARY since holding the control key is a common method for emulating a right click. So the if statement should be :
if ( event.getButton() == MouseButton.SECONDARY || e.isControlDown() ) {
// DO RIGHT CLICK ACTION
}
I'm developing a custom TreeView object.
I'm using a custom cellFactory to provide the TreeCell objects of my TreeView.
This allows me to install custom Context Menu on the various cells, depending on the Item they are displaying.
But I'm not entirely satisfied with the behaviour.
When left-clicking on cell, it gets selected (OK)
But when right-clicking a cell, the context menu is displayed (OK) but the cell is also selected. (NOK)
How can I change this behaviour ?
I tried to implement an eventFilter on the tree view, to consume the event if it is a right-click but this doesn't change anything, the above behaviour still applies.
addEventFilter(MouseEvent.MOUSE_CLICKED,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.SECONDARY) {
event.consume();
}
}
});
setCellFactory(new Callback<TreeView<TreeDisplayable>, TreeCell<TreeDisplayable>>() {
#Override
public TreeCell<TreeDisplayable> call(
final TreeView<TreeDisplayable> treeView) {
return new TreeDisplayableTreeCell(owner, javaModel);
}
});
public class TreeDisplayableTreeCell extends TreeCell<TreeDisplayable> {
...
#Override
public void updateItem(TreeDisplayable item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(getItem().treeViewString());
setGraphic(item.getPic());
if (getTreeItem().getParent() == null) {
// it means it's the root node
setContextMenu(new RootItemContextMenu(javaModel, owner));
} else {
setContextMenu(new TreeItemContextMenu(javaModel, owner,getTreeItem().getValue()));
}
}
}
}
Reacting on Tony's comment
Creating a custom EventDispatcher does the trick.
public class TreeEventDispatcher implements EventDispatcher {
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
if (mouseEvent.getButton() == MouseButton.SECONDARY) {
event.consume();
} else {
event = tail.dispatchEvent(event);
}
} else {
event = tail.dispatchEvent(event);
}
return event;
}
}
The behaviour is identical for all events, except the right click event, which is consumed, thus preventing the right-click selection of any TreeCell.
Luckily enough, the context menu is still displayed on right click (although I don't understand why ...) Does anybody have a clue ?
Previous Facewindu answer is actually working, but there is another way to achieve that behavior and still have context menu appearing on right click:
treeView.addEventFilter(MOUSE_PRESSED, event -> {
if (event.isSecondaryButtonDown()) {
Node text = (Node) event.getTarget();
TreeCell<...> treeCell = (TreeCell<...>) text.getParent();
treeCell.getContextMenu().show(treeCell, 0, 0);
event.consume();
}
});
I have a simple Windows Form: 'Message' TextBox has Enter and Leave events to allow user to enter text in another language only on that field. 'Send' button sends the form content. After the user fills the Message and clicks Send, Textbox's Leave event prevent button's Click event from firing. I need both handlers to run.
Here's the relevant code:
private void Message_Enter(object sender, EventArgs e)
{
inputLang = InputLanguage.CurrentInputLanguage;
foreach (InputLanguage lang in InputLanguage.InstalledInputLanguages)
{
if (lang.LayoutName == "United States-International")
{
InputLanguage.CurrentInputLanguage = lang;
break;
}
}
}
private void Message_Leave(object sender, EventArgs e)
{
InputLanguage.CurrentInputLanguage = inputLang;
}
private void Send_Click(object sender, EventArgs e)
{
string dest = ServerList.Text;
string msg = Message.Text;
if (dest.Length == 0 || msg.Length == 0 )
{
Log("Fill the destination server and the message");
return;
}
if (context.SendMessage(dest, msg))
{
if (!ServerList.Items.Contains(dest))
{
ServerList.Items.Add(dest);
}
}
else
{
if (ServerList.Items.Contains(dest))
{
ServerList.Items.Remove(dest);
}
}
}
The problem is now solved. The problem is caused by the change of input language. If the enter and leave handlers did other stuff then the click event will fire normally. Since I need to change the input language I solved it by monitoring the MouseDown, MouseClick and MouseUp events and generating a click if it was not automatically generated.
I've got same problem. When I changed the input language and then on leave event set it back to default one. When i click on other component it wont fire click event. I had to click twice then.
I thing it has something to do with focus.
I solved it by setting the focus back to form, after changing back the input language.
Here is the handler:
void textBoxSearch_LostFocus(object sender, EventArgs e)
{
InputLanguage.CurrentInputLanguage = InputLanguage.DefaultInputLanguage;
this.Focus();
}
Hope it helps...
Within a Windows Phone 7 app, is it possible to capture the hardware camera button pressed event in code?
Right now when I press the camera button nothing happens and I can't figure out how to hook up to the event.
Yes you can. Check this link. This is an example of the events:
// The event is fired when the shutter button receives a half press.
CameraButtons.ShutterKeyHalfPressed += OnButtonHalfPress;
// The event is fired when the shutter button receives a full press.
CameraButtons.ShutterKeyPressed += OnButtonFullPress;
// The event is fired when the shutter button is released.
CameraButtons.ShutterKeyReleased += OnButtonRelease;
// Provide auto-focus with a half button press using the hardware shutter button.
private void OnButtonHalfPress(object sender, EventArgs e)
{
if (cam != null)
{
// Focus when a capture is not in progress.
try
{
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Half Button Press: Auto Focus";
});
cam.Focus();
}
catch (Exception focusError)
{
// Cannot focus when a capture is in progress.
this.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = focusError.Message;
});
}
}
}