Ongoing work on supporting GTK, had to rework some concepts due to the way gtk-rs works.

This commit is contained in:
Ryan McGrath 2019-06-08 13:56:39 -07:00
parent f15cf258af
commit 0d8a14ce67
No known key found for this signature in database
GPG key ID: 811674B62B666830
20 changed files with 378 additions and 301 deletions

19
gtk/Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "alchemy-gtkrs"
description = "GTK bindings for Alchemy, a cross-platform GUI framework written in Rust."
version = "0.1.0"
edition = "2018"
authors = ["Ryan McGrath <ryan@rymc.io>"]
license = "MPL-2.0+"
repository = "https://github.com/ryanmcgrath/alchemy"
categories = ["gui", "rendering::engine", "multimedia"]
keywords = ["gui", "gtk", "react"]
[badges]
maintenance = { status = "actively-developed" }
[dependencies]
alchemy-lifecycle = { version = "0.1", path = "../lifecycle", features = ["gtkrs"] }
alchemy-styles = { version = "0.1", path = "../styles" }
gtk = { version = "0.6.0", features = ["v3_16"] }
gio = "0.6.0"

44
gtk/src/app.rs Normal file
View file

@ -0,0 +1,44 @@
//! A wrapper for `Application` on GTK-based systems. If you opt in to the `gtkrs` feature on
//! Alchemy, this will loop system-level application events back to your `AppDelegate`.
use std::env;
use gio::{ApplicationFlags};
use gtk::{Application};
use gio::prelude::{ApplicationExt, ApplicationExtManual};
use alchemy_lifecycle::traits::AppDelegate;
/// A wrapper for `gtk::Application`.
pub struct App {
pub inner: Application
}
impl App {
/// Creates a `gtk::Application` instance, and wires up appropriate lifecycle handlers to loop
/// back to the `AppDelegate`.
pub fn new<T: AppDelegate + 'static>(parent_app_ptr: *const T) -> Self {
let inner = Application::new("lol.my.app", ApplicationFlags::FLAGS_NONE)
.expect("Could not create GTK Application instance!");
inner.connect_activate(move |app| {
println!("ACTIVATED");
let app = parent_app_ptr as *mut T;
unsafe {
(*app).did_finish_launching();
}
println!("HELLO");
});
println!("MADE");
App {
inner: inner
}
}
/// Kicks off the Run Loop for the Application instance. This blocks when called.
pub fn run(&self) {
println!("RUNNING");
self.inner.run(&env::args().collect::<Vec<_>>());
}
}

22
gtk/src/lib.rs Normal file
View file

@ -0,0 +1,22 @@
//! This crate provides a GTK backend for Alchemy, the Rust GUI framework.
//! This means that, on GTK-based systems, you'll be using native views
//! and other assorted controls. Where possible, it attempts to opt into
//! smoother rendering paths.
//!
//! # License
//!
//! Copyright 2019 Ryan McGrath. See the license files included in the root repository
//! for more information, along with credit to applicable parties for who this project
//! would not have happened.
//!
//! # Code of Conduct
//!
//! Please note that this project is released with a [Contributor Code of
//! Conduct][coc]. By participating in this project you agree to abide by its terms.
//!
//! [coc]: https://www.contributor-covenant.org/version/1/4/code-of-conduct
pub mod app;
pub mod text;
pub mod view;
pub mod window;

47
gtk/src/text.rs Normal file
View file

@ -0,0 +1,47 @@
//! This wraps NTextField on macOS, and configures it to act like a label
//! with standard behavior that most users would expect.
use alchemy_styles::{Color, Layout, Appearance};
use alchemy_lifecycle::traits::PlatformSpecificNodeType;
static ALCHEMY_DELEGATE: &str = "alchemyDelegate";
/// A wrapper for `NSText`. This holds retained pointers for the Objective-C
/// runtime - namely, the view itself, and associated things such as background
/// colors and so forth.
#[derive(Debug)]
pub struct Text {
}
impl Text {
/// Allocates a new `NSTextField` on the Objective-C side, ensuring that things like coordinate
/// flipping occur (macOS still uses (0,0) as lower-left by default), and opting in to layer
/// backed views for smoother scrolling.
pub fn new() -> Text {
Text {
}
}
/// Returns a pointer to the underlying Objective-C view. The pointer is not mutable; however,
/// you can send messages to it (unsafely).
pub fn borrow_native_backing_node(&self) -> PlatformSpecificNodeType {
()
}
/// Appends a child NSText (or subclassed type) to this view.
pub fn append_child(&mut self, child: PlatformSpecificNodeType) {
}
/// Given a `&Style`, will set the frame, background color, borders and so forth. It then
/// calls `setNeedsDisplay:YES` on the Objective-C side, so that Cocoa will re-render this
/// view.
pub fn apply_styles(&mut self, appearance: &Appearance, layout: &Layout) {
}
pub fn set_text(&mut self, text: String) {
}
pub fn render(&mut self) {
}
}

38
gtk/src/view.rs Normal file
View file

@ -0,0 +1,38 @@
//! Implements a View Component struct. The most common
//! basic building block of any app. Wraps NSView on macOS.
use alchemy_styles::{Appearance, Color, Layout};
use alchemy_lifecycle::traits::PlatformSpecificNodeType;
/// A wrapper for `NSView`. This holds retained pointers for the Objective-C
/// runtime - namely, the view itself, and associated things such as background
/// colors and so forth.
#[derive(Debug)]
pub struct View {}
impl View {
/// Allocates a new `NSView` on the Objective-C side, ensuring that things like coordinate
/// flipping occur (macOS still uses (0,0) as lower-left by default), and opting in to layer
/// backed views for smoother scrolling.
pub fn new() -> View {
View {
}
}
/// Returns a pointer to the underlying Objective-C view. The pointer is not mutable; however,
/// you can send messages to it (unsafely).
pub fn borrow_native_backing_node(&self) -> PlatformSpecificNodeType {
()
}
/// Appends a child NSView (or subclassed type) to this view.
pub fn append_child(&mut self, child: PlatformSpecificNodeType) {
}
/// Given a `&Style`, will set the frame, background color, borders and so forth. It then
/// calls `setNeedsDisplay:YES` on the Objective-C side, so that Cocoa will re-render this
/// view.
pub fn apply_styles(&mut self, appearance: &Appearance, layout: &Layout) {
}
}

73
gtk/src/window.rs Normal file
View file

@ -0,0 +1,73 @@
//! Implements a `gtk::ApplicationWindow` wrapper for GTK-based systems.
//! This also handles looping back lifecycle events, such as window
//! resizing or close events.
use std::cell::RefCell;
use gtk::{
ContainerExt,
GtkWindowExt, WidgetExt,
Window as GtkWindow, WindowType
};
use alchemy_lifecycle::traits::WindowDelegate;
use alchemy_styles::Appearance;
/// A wrapper for `NSWindow`. Holds (retains) pointers for the Objective-C runtime
/// where our `NSWindow` and associated delegate live.
pub struct Window {
pub inner: GtkWindow
}
impl Window {
/// Creates a new `NSWindow` instance, configures it appropriately (e.g, titlebar appearance),
/// injects an `NSObject` delegate wrapper, and retains the necessary Objective-C runtime
/// pointers.
pub fn new<T: WindowDelegate>(content_view: (), app_ptr: *const RefCell<T>) -> Window {
Window {
inner: GtkWindow::new(WindowType::Toplevel)
}
}
pub fn set_title(&mut self, title: &str) {
self.inner.set_title(title);
}
pub fn set_dimensions(&mut self, x: f64, y: f64, width: f64, height: f64) {
self.inner.set_position(gtk::WindowPosition::Center);
self.inner.set_default_size(width as i32, height as i32);
}
/// Normally used for setting platform-specific styles; on macOS we choose not to do this and
/// just have the content view handle the background color, as calling window
/// setBackgroundColor causes some notable lag on resizing.
pub fn apply_styles(&mut self, _appearance: &Appearance) { }
/// On macOS, calling `show()` is equivalent to calling `makeKeyAndOrderFront`. This is the
/// most common use case, hence why this method was chosen - if you want or need something
/// else, feel free to open an issue to discuss.
///
/// You should never be calling this yourself, mind you - Alchemy core handles this for you.
pub fn show(&self) {
let button = gtk::Button::new_with_label("Click me!");
self.inner.add(&button);
self.inner.show_all();
}
/// On macOS, calling `close()` is equivalent to calling... well, `close`. It closes the
/// window.
///
/// I dunno what else to say here, lol.
///
/// You should never be calling this yourself, mind you - Alchemy core handles this for you.
pub fn close(&self) {
}
}
impl Drop for Window {
/// When a Window is dropped on the Rust side, we want to ensure that we break the delegate
/// link on the Objective-C side. While this shouldn't actually be an issue, I'd rather be
/// safer than sorry.
fn drop(&mut self) {
}
}