Gtk.DrawingArea mouse events using Vala - mouseevent

I have been trying to receive mouse events on my Gtk.DrawingArea, using Vala, with no success. Specifically I am sub-classing Gtk.DrawingArea and in my constructor I add the events I want to receive:
this.add_events (Gdk.EventMask.ENTER_NOTIFY_MASK |
Gdk.EventMask.BUTTON_PRESS_MASK);
Then, in the same constructor below, I register signal handlers for these events:
this.enter_notify_event.connect (
(page, event) => {
stdout.printf("mouse entered !!! \n");
return true;
}
);
this.button_press_event.connect (
(page, event) => {
stdout.printf("mouse click \n");
return false;
}
);
I tried both return true and return false to check what happens in both cases. However I see no messages on the console when I move the pointer on the Gtk.DrawingArea or when I click on it. I even set the events for the top Gtk.Window:
this.set_events (this.get_events() |
Gdk.EventMask.ENTER_NOTIFY_MASK |
Gdk.EventMask.BUTTON_PRESS_MASK);
but the events don't seem to get received. What could be going wrong?

Your code seems correct although it's not a MVCE. I would point out the callback handlers prototype as being incorrect but since you're not using event data it should not be a "problem". The callback prototypes for enter_notify_event and button_press_eventonly supply the event, so the page argument it's incorrect.
Anyway, i tested with a very simple and raw code and it worked. Please verify:
using Gtk;
public class MyWidget : Gtk.DrawingArea {
public MyWidget () {
this.set_events (Gdk.EventMask.ENTER_NOTIFY_MASK |
Gdk.EventMask.BUTTON_PRESS_MASK);
this.enter_notify_event.connect ((event) => {
stdout.printf ("mouse entered !!! \n");
return false;
});
this.button_press_event.connect ((event) => {
stdout.printf("mouse click \n");
return false;
});
}
}
public void main (string[] args) {
Gtk.init (ref args);
var window = new Gtk.Window ();
window.add (new MyWidget ());
window.destroy.connect (Gtk.main_quit);
window.show_all ();
Gtk.main ();
}
Compile with valac test.vala --pgk gtk+-3.0.
The result is:
Using Vala 0.30.2 and Gtk+ 3.18 on Fedora 23.

Related

Flutter Mockito verify that callback passed to widget is called

I have a widget that takes a callback which is called when a button is pressed. I am trying to test that the callback is correctly invoked by the button.
I've tried mocking a Function class:
class MockCallback extends Mock implements Function {
call() {}
}
Then passing an instance of the mock class to my widget and simulating a tap:
final mocked = MockCallback();
await tester.pumpWidget(
MyWidget(myCallback: mocked),
);
final clearButtonFinder = find.byType(IconButton);
await tester.tap(clearButtonFinder);
verify(mocked()).called(1);
This results in an error on the verify call saying Used on a non-mockito object. If I put a print statement inside the mocked call, I can see that the tap is indeed calling it.
How can I verify that the callback passed to my widget is getting called once when the button is tapped?
This is how I solved this problem.
class MockCallback {
int _callCounter = 0;
void call() {
_callCounter += 1;
}
bool called(int expected) => _callCounter == expected;
void reset() {
_callCounter = 0;
}
}
No mockito needed.
Probably it is not the best solution - use a stream:
final callbackCalled = BehaviorSubject<void>.seeded(null);
await tester.pumpWidget(
MyWidget(myCallback: () { callbackCalled.add(null); }),
);
//... actions to trigger the callback
await expectLater(callbackCalled, emitsInOrder(<void>[null, null]));
You can use something meaningful instead of 'void' and 'null'.

Accessing Context inside an ExchangeFilterFunction

For some reason a context inside the doAfterSuccessOrError method is not available (populated) from the upstream. I've tried to access it using Mono.subscriberContext() (see the snipped). I would expect to have it present but for some reason is not. Am I doing something wrong?
public class LoggingRequestExchangeFunction implements ExchangeFilterFunction {
private final Logger log = LoggerFactory.getLogger(getClass());
#Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
long start = System.currentTimeMillis();
return next.exchange(request).doAfterSuccessOrError((res, ex) -> {
Mono.subscriberContext().map((ctx -> {
log.info("doAfterSuccessOrError Context {}",ctx);
// log req/res ...
return ctx;
})).subscribe();
}).subscriberContext( ctx -> {
log.info("SubscriberContext: {}" , ctx);
return ctx;
});
}
}
Here is a log output
23:16:59.426 INFO [reactor-http-epoll-2] .p.c.LoggingRequestExchangeFunction [] SubscriberContext: Context1{nexmo-tracing-context=TracingContext{{traceId=f04961da-933a-4d1d-85d5-3bea2c47432f, clientIp=N/A}}}
23:16:59.589 INFO [reactor-http-epoll-2] .p.c.LoggingRequestExchangeFunction [] doAfterSuccessOrError Context Context0{}
The reason is that you create a new Mono inside doAfterSuccessOrError which is independent from the original reactor chain since you subscribe to it separately.
If you just want to log something inside, your alternative is to use doOnEach operator which beside the signal type gives you access to the context as well.
Mono.just("hello")
.doOnEach((signal) ->
{
if (signal.isOnError() || signal.isOnComplete())
{
Context ctx = signal.getContext();
log.info("doAfterSuccessOrError Context {}",ctx);
// log req/res ...
}
})
.subscriberContext( ctx -> {
log.info("SubscriberContext: {}" , ctx);
return ctx;
})
.subscribe();

How to display a Labels 'Error on ErrorProvider1'

Goal
I want to display the text that I put in the Label's "Error on ErrorProvider1" attribute whenever I get an error. See the following label's attributes below.
I try to display the text in the red rectangle into my ErrorProvider1 SetError(control, value) function.
If TextBox1.Text.Trim.Contains("'") Then
ErrorProvider1.SetError(lblErr, ErrorProvider1.GetError(lblErr))
Else
ErrorProvider1.SetError(lblErr, "")
End If
How can I retrieve the 'Error on ErrorProvider1' text from the lblErr to display it in the ErrorProvider1 SetError value?
The ErrorProvider component is very awkward to use effectively. It is fixable however, I'll give an example in C# that extends the component with some new capabilities:
ShowError(Control ctl, bool enable) displays the text that you entered at design-time when the enable argument is true. The easier-to-use version of SetError().
HasErrors returns true if the any active warning icons are displayed. Handy in your OK button's Click event handler.
FocusError() sets the focus to the first control that has a warning icon, if any. It returns false if no warnings are remaining.
SetError() is a replacement of ErrorProvider.SetError(). You only need it if you add any controls after the form's Load event fired or if you need to modify the warning text.
Add a new class to your project and paste the code shown below. Compile. Drop it from the top of the toolbox onto the form. The design-time behavior is identical. Modestly tested.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.ComponentModel.Design;
class MyErrorProvider : ErrorProvider {
public void ShowError(Control ctl, bool enable) {
// Easy to use version of SetError(), uses design-time text
if (!enable) base.SetError(ctl, "");
else {
if (errors.ContainsKey(ctl)) base.SetError(ctl, errors[ctl]);
else base.SetError(ctl, "No error text available");
}
}
public bool HasErrors {
// True if any errors are present
get {
foreach (var err in errors)
if (!string.IsNullOrEmpty(base.GetError(err.Key))) return true;
return false;
}
}
public bool FocusError() {
// Set the focus to the first control with an active error
foreach (var err in errors) {
if (!string.IsNullOrEmpty(base.GetError(err.Key))) {
err.Key.Focus();
return true;
}
}
return false;
}
public new void SetError(Control ctl, string text) {
// Use this only to add/modify error text after the form's Load event
if (!string.IsNullOrEmpty(text)) {
if (errors.ContainsKey(ctl)) errors[ctl] = text;
else errors.Add(ctl, text);
}
base.SetError(ctl, text);
}
private void initialize(object sender, EventArgs e) {
// Preserve error text
copyErrors(((Form)sender).Controls);
}
private void copyErrors(Control.ControlCollection ctls) {
foreach (Control ctl in ctls) {
var text = this.GetError(ctl);
if (!string.IsNullOrEmpty(text)) {
errors.Add(ctl, text);
base.SetError(ctl, "");
}
copyErrors(ctl.Controls);
}
}
private Dictionary<Control, string> errors = new Dictionary<Control, string>();
// Plumbing to hook the form's Load event
[Browsable(false)]
public new ContainerControl ContainerControl {
get { return base.ContainerControl; }
set {
if (base.ContainerControl == null) {
var form = value.FindForm();
if (form != null) form.Load += initialize;
}
base.ContainerControl = value;
}
}
public override ISite Site {
set {
// Runs at design time, ensures designer initializes ContainerControl
base.Site = value;
if (value == null) return;
IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (service == null) return;
IComponent rootComponent = service.RootComponent;
this.ContainerControl = rootComponent as ContainerControl;
}
}
}
Your issue is that you are replacing the error message when nothing is wrong. As noted in your comment below, you are storing the localized error message in the label's Tag, so you can do the following:
If TextBox1.Text.Trim.Contains("'") Then
ErrorProvider1.SetError(lblErr, lblErr.Tag)
Else
ErrorProvider1.SetError(lblErr, "")
End If
You were correct to use ErrorProvider1.GetError(Control) to get the value. It's just that you're more than likely replacing it with an empty string before you were retrieving it.

Show a Dialog() or MessageDialog() from the MainWindow.cs?

I'm trying to display a Dialog() or MessageDialog() from my MainWindow.cs (standard MainWindow.cs file that is created from the C# GTK# 2.0 Project template).
I'm getting a rather nasty error when using the below code:
public partial class MainWindow : Gtk.Window
{
public MainWindow () : base(Gtk.WindowType.Toplevel)
{
Build();
}
public void CreateAlert(string message)
{
Console.WriteLine(string.Format("CreateAlert() - message: {0}", message));
Dialog dialog = new Dialog("Error", this, Gtk.DialogFlags.DestroyWithParent);
dialog.Modal = true;
dialog.AddButton ("OK", ResponseType.Close);
dialog.Response += on_dialog_response;
dialog.Run ();
dialog.Destroy ();
}
}
I believe the cause of this error is the second param in the Dialog() constructor - "this". My question is... how does one satisfy the second param of "Window win" when inside the MainWindow.cs?
Thanks in advance.

WebView title is sometimes null when it shouldn't be

I'm writing a simple browser in Vala and WebKitGTK+.
One of the things I need to do is set the window's title to that of the webpage title, so I monitor title changes with web_view.notify["title"].connect. However, sometimes the value of title is null, when it obviously shouldn't be.
Some examples I remember:
Search anything in Google. Going to the next results page sets the title to null.
Click on an anchor link (e.g. http://example.com/page.html#section)
In any case, using the Web Inspector shows that the pages do have a title set.
Is this a bug I should report? Or maybe I'm doing something wrong? Here's the code I'm using:
//valac --thread --pkg webkitgtk-3.0 --pkg gtk+-3.0 --vapidir=./ test.vala
//The vapidir folder should have webkitgtk-3.0.vapi and webkitgtk-3.0.deps
using WebKit;
using Gtk;
public class Test : Gtk.Window {
public WebView webview;
public Test () {
this.title = "Test";
this.window_position = Gtk.WindowPosition.CENTER;
this.set_default_size (800, 600);
this.hide_titlebar_when_maximized = false;
this.destroy.connect (() => {
Gtk.main_quit ();
});
var scroll = new ScrolledWindow (null, null);
this.webview = new WebView ();
this.add (scroll);
scroll.add (webview);
webview.settings.enable_developer_extras = true;
webview.notify["title"].connect ((sender, property) => {
if (webview.title != null) {
this.title = webview.title;
stdout.printf (webview.title + "\n");
} else {
stdout.printf ("(null)\n");
}
});
webview.web_inspector.inspect_web_view.connect ((p0) => {
var w = new Window ();
w.window_position = WindowPosition.CENTER;
w.title = "Inspector";
w.set_default_size (800,600);
WebView view = new WebView ();
unowned WebView view2 = view;
w.add (view2);
w.show_all ();
return view2;
});
}
public static int main (string[] args) {
Gtk.init (ref args);
Test app = new Test ();
app.webview.load_uri ("http://google.com");
app.show_all ();
Gtk.main ();
return 0;
}
}
There is a signal called title_changed for that purpose and you should always use the signals provided by webkitgtk instead of the GLib notify feature.
(GLib notify is emitted each time a value changes, even if the change is just for the purpose of clearing the old value, such as in your case.)