gtk-rs: Does a gtk4 widget hold a reference to its callbacks while a gtk3 widget doesn't? - gtk-rs

I am porting a working gtk-rs application from gtk3 to gtk4. The following code is a reduced self-contained example to demonstrate the Problem. The code is for gtk3 with the changes required for gtk4 indicated by comments.
use gtk::prelude::*;
use gtk::{ApplicationWindow, DrawingArea};
use std::cell::RefCell;
use std::rc::Rc;
struct Ctx {
text: String,
_drawing_area: gtk::DrawingArea,
}
impl Drop for Ctx {
fn drop(&mut self) {
println!("DROP");
}
}
fn on_activate(app: &gtk::Application) {
let drawing_area = DrawingArea::builder().build();
let win = ApplicationWindow::builder()
.application(app)
.child(&drawing_area)
.build();
// gtk3:
win.show_all();
// gkt4:
//win.show();
let ctx = Ctx {
text: "Hallo".into(),
_drawing_area: drawing_area.clone(),
};
let shared_context = Rc::new(RefCell::new(ctx));
{
let shared_context = shared_context.clone();
// gtk3:
drawing_area.connect_draw(move |_area, _cairo| {
let ctx = shared_context.borrow();
println!("draw text= \"{}\"", ctx.text);
gtk::Inhibit(false)
});
// gtk4:
//drawing_area.set_draw_func(move |_area, _cairo, _width, _height| {
// let ctx = shared_context.borrow();
// println!("draw text= \"{}\"", ctx.text);
//});
}
}
fn main() {
let app = gtk::Application::builder().build();
app.connect_activate(on_activate);
app.run();
println!("main loop finished");
}
With gtk3, the code works as intended: The Ctx structure is dropped when the window is closed:
Running `target/debug/droptest`
draw text= "Hallo"
DROP
main loop finished
With gtk4, however, the Ctx structure is not dropped:
Running `target/debug/droptest`
draw text= "Hallo"
main loop finished
I see, that there is a circular reference ctx -> drawing_area -> draw_func -> closure -> shared_context -> ctx. I assume that is the reason, that ctx is not dropped with gtk4.
However, I'd expected gtk3 and gtk4 (-rs) to behave identically. Where does the difference come from? Is it documented?
Any suggestions to solve the problem with gtk4? The closure could get a weak reference, but then ctx would be dropped when on_active() is finished. Is there an elegant way to reference ctx from the ApplicationWindows once? In reality, of course, Ctx contains many widgets and other data and there are multiple callbacks.

As nobody jumped on this, I try to answer that myself:
The observed different behavior can be demonstrated with the C api of gtk as well, it is not rooted in gtk-rs. Circular reference are not reliably resolved by the gtk runtime, although sometimes it works with gtk3 when a window is closed.
See Why does gtk3 call the ClosureNotify function in the example code, but gkt4 doesn't?
The possible approaches to resolve the problem of the reference cycle mentioned in the link above work for gtk-rs, too:
Subclass one of the widgets and make the ctx data part of it
For gtk4: Use a "win.close" action
Use weak references in ctx

Related

Method/property does not exist

I'm trying to convert the JavaScript code
if (window.ifEdit.editIsDirty()) { }
into Typescript. I got as far as the following
var iframe = document.getElementById('ifEdit');
var iWindow = <HTMLIFrameElement>(iframe).contentWindow;
var _editIsDirty = iWindow.editIsDirty();
I get the red squiggles under 'contentWindow' and 'editIsDirty' saying the method/property does not exist on the type. The .ts doesn't compile to a .js file.
I have searched, but did not manage to find a solution.
For the contentWindow part, the problem with your code is that the casting is done wrong, should be:
var iWindow = (<HTMLIFrameElement> iframe).contentWindow;
As for the editIsDirty, it's not a standard property of Window.
If it's something which is added in the environment in which you are running your javascript then you need to declare it like so:
interface IfEdit {
editIsDirty(): boolean;
}
interface Window {
ifEdit: IfEdit;
}
var iframe = document.getElementById("ifEdit");
var iWindow = (<HTMLIFrameElement> iframe).contentWindow;
var _editIsDirty = iWindow.ifEdit.editIsDirty();
Use the code in Playground.
Casting will be through as. this assures .contentWindow is accessible.
const iframe = document.getElementById('embed-player') as HTMLIFrameElement;
if (!iframe) {
// Handle case where iframe not found
return;
}
const contentWindow = iframe.contentWindow;
// Note: You will likely need more null handling for contentWindow's properties
console.log(contentWindow?.document);

Meteor - Reactive Objects/Classes

TLDR
I like to really focus on keeping business logic away from the view model / controller. I find this sometimes rather hard in Meteor. Maybe I'm missing the point but I am after one of two things really:
1) A really good document explaining at a really low level how reactive values are being used.
2) A package that somehow manages an object so that if any of the setters are altered, they notify all of the get functions that would change as a result.
Unfortunately I've not seen either.
My Example
I have a fair bit ob business logic sitting behind a dialog used to document a consultation. I might have an event that sets a change of state.
I'd like to do something like this in the event:
const cc = new ConsultationEditor();
cc.setChiefComplaint(event.target.value);
console.log(cc.data());
ConsultationDict.set("consEdit", cc.data() );
When the user has updated this value, I'd then like to show a number of fields, based on the change. For this I have a helper with the following:
fields: function(){
console.log("trying to get fields");
const obj = ConsultationDict.get('consEdit');
cc = new ConsultationEditor(obj);
return cc.getFields();
}
But unfortunately this does not work for me.
What is your ConsultationDict?
The way you describe it, you want it to be a ReactiveDict as in the official ReactiveDict package.
https://atmospherejs.com/meteor/reactive-dict
Check this tutorial for examples:
https://themeteorchef.com/snippets/reactive-dict-reactive-vars-and-session-variables/
If you really need more fine tuning in your reactivity, you can also set a dependency tracker tracker = new Tracker.Dependency, and then refer to it wherever you change a variable with tracker.changed() and where the data needs to be notified with tracker.depend() like this:
var favoriteFood = "apples";
var favoriteFoodDep = new Tracker.Dependency;
var getFavoriteFood = function () {
favoriteFoodDep.depend();
return favoriteFood;
};
var setFavoriteFood = function (newValue) {
favoriteFood = newValue;
favoriteFoodDep.changed();
};
getFavoriteFood();
See the full Tracker doc here:
https://github.com/meteor/meteor/wiki/Tracker-Manual
I also found this gist to be useful to build reactive objects:
https://gist.github.com/richsilv/7d66269aab3552449a4c
and for a ViewModel type of behavior, check out
https://viewmodel.meteor.com/
I hope this helps.

Select option value - selectedchoice is not working

I am binding a SELECT HTML tag with some dynamic values using knockout JS. Additionally, i am trying to set a selected choice which is failing. Please suggest where i am going wrong.
self.level1Choices.selectedChoice = ko.observable(2); - this line does not seem to work.
The JSFiddle for this code is at http://jsfiddle.net/oarp7gwj/7/
The dropdown is not loading in the JSFiddle for some reason. I dont think i have referenced the knockout JS correctly. In my local environment, I am able to load the select box with the values. However, i am not able to set the selected value.
#Wayne Ellery, #QBM5 - please advise since you know about this already :)
You should use var to declare your model object to avoid scoping issues
var viewModel = new DataModel();
The main issue was you need to add to the Datamodel by exposing it through the this variable in the Datamodel.
var DataModel = function (client) {
var self = this;
self.level1Choices = ko.observableArray();
};
Take a look at the Helo World example as to how to do this:
http://knockoutjs.com/examples/helloWorld.html
I've scoped this to self as it's a best practice to not worry about this referring to something else mentioned here: http://knockoutjs.com/documentation/computedObservables.html.
I moved the loadAllApprovers method inside the DataModel as this is where it belongs and so that it has access to populate the datamodel.
I added the mobile services client to the constructor so that it can be mocked for testing your model.
var DataModel = function (client) {
var self = this;
self.level1Choices = ko.observableArray();
var loadAllApprovers = function () {
var allAppprovers = client.getTable('TABLE');
var query = allAppprovers.select("ID", "FirstName").read().done(function (approverResults) {
self.level1Choices(approverResults);
}, function (err) {
console.log("Error: " + err);
});
};
loadAllApprovers();
};
You were also missing knockout in your jsfiddle.
http://jsfiddle.net/az4rox0q/6/

Handlebars.js: how does partials gets invoked in a helper? I got: TypeError: fn is not a function

I am learning Handlebars. It still appears to be a mystery to me that how a partials gets invoked in a helper.
I read this tutorial: http://blog.teamtreehouse.com/handlebars-js-part-2-partials-and-helpers
From this example in the tutorial,
Handlebars.registerHelper("stripes", function(array, even, odd, fn) {
var buffer = "";
for (var i = 0, j = array.length; i < j; i++) {
var item = array[i];
// we'll just put the appropriate stripe class name onto the item for now
item.stripeClass = (i % 2 == 0 ? even : odd);
// show the inside of the block
buffer += fn(item); <!-- this is where a partials gets invoked -->
}
// return the finished buffer
return buffer;
});
it appears the partial is added and applied by Handlebars. However, I used this same approach in Handablebars 1.3.0 and 2.0 Alpha-2, it seems no longer the case. I always got the error:
TypeError: fn is not a function
buffer += fn(item);
I did quite online search and found a number tutorials, but none of them shows how partials is hooked up with a helper in version 1.3.0 or later.
Can someone help me out?
Thanks a lot!
OK. I believed I solved this problem. In v1.0 or later, Handlebars puts everything in a hash, not just fn. So, everything in the above post still is valid except these two lines:
Handlebars.registerHelper("stripes", function(array, even, odd, options)
...
buffer += options.fn(item);
Hope this helps someone else. Any confirmation is welcome. I feel bad that there is no direct example on this, at least I did not find it.
Regards.

JSHint and "Tolerate Variable Shadowing"

Does anybody know why this code does not produce errors in JSHint?
I think it should give me a variable shadowing warning but I'm not getting one.
I have "Tolerate Variable shadowing" as false am using the visual studio plugin.
RES.test = function () {
var test, f;
f = function () {
var test;
window.alert(test);
};
};
Thanks.
I just stumbled onto this too. Apparently JSHint developers' definition of "shadowing" is not what you expect. For them, hiding a variable name coming from a closure is not shadowing. And yes, I find it strange too :-)
If you look at their test suite, they mean something like "redefinition", where you do
var a = 1;
...
var a = 2;
look at their test case: https://github.com/jshint/jshint/blob/master/tests/stable/unit/fixtures/redef.js

Resources