Open ArekPiekarz opened 4 years ago
Whoa, awesome. Thanks for your effort - it means a lot. :+1:
Let me respond to your points one by one:
.iter()
call on each for loop - your hint makes things definitely better. :grin:rand
and png
were initially used to generate some random art and save it to a file before I figured out how to paint stuff on a window, and now are dead leftovers. Will have them removed.Current state:
1.
I will take care of the other points soon. :)
I gave a bit of thought to the point 7, and I think I'll leave it as it is now, for now at least - for the sake of a just slightly nicer call (&args().collect_vec()
vs &args().collect::<Vec<_>>()
) in an uninteresting place of code we'd be adding a crate dependency and an additional use statement, which seems a bit of an overkill to me. But I'll keep in mind that crate - possibly it's going to be useful sometime later. :)
1.
1.
Regarding point 7 I've developed a proof of concept on a branch (see commit 08322be3e943ea65d1dc15543c50c444eb3c1b6e), although so far I've failed to entirely get rid of the need for reference counting of the model.
The model is needed in two event handlers now: one is the newly introduced events reactor, and the other is the functor that redraws the drawing area. I've tried to make redraw an another event, but then the reference to the cairo context would have to be passed as a part of the event, which fails miserably since the event seems to outlive the call to the drawing functor. I've also tried to pass a copy of the cairo context (as it is copyable), but no success there either as it pretty much results in drawing on a dev null - the application window never gets any drawing.
Any ideas @ArekPiekarz ?
Since the code on the branch is half done (it does not entirely get rid of the Rc-s as I hoped for) I haven't merged it to the master yet, but I tend to consider it a bit better anyway - even though the Rc-s are still there, all the actions (except for the redraw) are handled in a single central place, which is kind of nice, isn't it?
The only potential solution I see from the documentation is manually invoking drawing instead of waiting for callback from connect_draw
.
Requirements for painting: gtk::DrawingArea
and cairo::Context
.
We already own the first one.
We can get cairo::Context
by using DrawingContext::get_cairo_context.
DrawingContext would be retrieved from WindowExt::begin_draw_frame - it's a trait, I guess implemented by a window class. Note it requires enabling feature v3_22
in gdk
in Cargo.toml
. WindowExt::begin_draw_frame
also requires cairo::Region
parameter, which could be created with Region::create_rectangle.
We would also need to call WindowExt::end_draw_frame.
Note that I haven't checked if it will actually work, so maybe keeping the Rc
for model would be easier after all.
Well, I've gave it a try (see this commit: c77cbfeeca083b198a44b2fe936c58f846ae16b5), but it resulted, just as my yesterday's idea (here's a commit showing the idea: cabb3d04d0742e9ae972385c693f0227c4d77162), in screen flickering - the more elements to draw, the more severe flickering. Initially when reading the documentation for that WindowExt::begin_draw_frame method I thought that was what I was missing in my approach, but apparently not.
Somehow I'm under an impression, that the thread serving events is not the one that should fiddle with UI drawing, and we simply end up in a race condition where two threads try to draw the window - one GTK specific - that is meant to do just that, and the other that we're in when handling events.
I'm sending a a review what could be improved in this project, in random order of importance.
We should include Cargo.lock file in binary projects to have reproducible builds. If it was a library project it would be better not to include it, so that the users of the library could decide in their binary projects the exact versions of dependencies.
The code below is not needed in Rust edition 2018, it was probably needed only in 2015.
These manually written operators:
impl std::ops::Mul for EuclideanVector {
type Output = Self;
}
impl std::ops::Div for EuclideanVector {
type Output = Self;
}
The only distadvantages are 1 more dependency and a little longer compile times from scratch. On my laptop they were:
debug mode:
release mode:
We can use
for x in &container
instead offor x in container.iter()
. For mutable access, it would befor x in &mut container
instead offor x in container.iter_mut()
. Another alternative iscontainer.iter().for_each(|x| x.doAction());
, but it's a matter of preference.We don't need to use
std::ptr::eq
here, because we can just compare references. In Rust they are not automatically dereferenced like in C++. This code:impl std::cmp::PartialEq for Body { fn eq(&self, other: &Self) -> bool { ptr::eq(self, other) } }
Example pseudocode:
I noticed that drawing requires the drawing area and Cairo context, so you may need to send them in the event, if that is possible. Maybe glib::MainContext::sync_channel will be required.
Using crate itertools we can turn this code:
into this:
After removing "extern crates" from main.rs and running cargo +nightly udeps it revealed that dependencies
rand
andpng
are not used and can be removed.We can further optimize dependencies by disabling default features and enabling only the ones we need:
This has the following effects:
We can run cargo clippy to find more things to improve:
cargo clippy -- -W clippy::pedantic -W clippy::style -W clippy::complexity -W clippy::correctness -W clippy::perf -W clippy::nursery
I didn't enable warning groups-W clippy::restriction
and-W clippy::cargo
. For example (real output gives more warnings):const fn
--> src/main.rs:97:5