Create a slideshow application in my cyberpunk style #252
File diff suppressed because it is too large
Load Diff
|
@ -9,6 +9,8 @@ members = [
|
||||||
"config",
|
"config",
|
||||||
"config-derive",
|
"config-derive",
|
||||||
"coordinates",
|
"coordinates",
|
||||||
|
"cyberpunk",
|
||||||
|
"cyberpunk-slideshow",
|
||||||
"cyberpunk-splash",
|
"cyberpunk-splash",
|
||||||
"dashboard",
|
"dashboard",
|
||||||
"emseries",
|
"emseries",
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "cyberpunk-slideshow"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cairo-rs = "0.20.1"
|
||||||
|
cyberpunk = { path = "../cyberpunk" }
|
||||||
|
gio = "0.20.4"
|
||||||
|
glib = "0.20.4"
|
||||||
|
gtk = { version = "0.7", package = "gtk4" }
|
||||||
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
|
serde_yml = "0.0.12"
|
|
@ -0,0 +1,20 @@
|
||||||
|
- text: The distinguishing thing about magic is that it includes some kind of personal element. The person who is performing the magic is relevant to the magic. -- Ted Chang, Marie Brennan
|
||||||
|
position: top
|
||||||
|
transition_in: !Fade
|
||||||
|
duration:
|
||||||
|
secs: 5
|
||||||
|
nanos: 0
|
||||||
|
- text: Any sufficiently advanced technology is indistinguishable from magic. -- Arthur C. Clark.
|
||||||
|
position: middle
|
||||||
|
transition_in: !Fade
|
||||||
|
duration:
|
||||||
|
secs: 5
|
||||||
|
nanos: 0
|
||||||
|
|
||||||
|
- text: Electricity is the closest we get to Magic in this world.
|
||||||
|
position: middle
|
||||||
|
transition_in: !Fade
|
||||||
|
duration:
|
||||||
|
secs: 5
|
||||||
|
nanos: 0
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
use std::{fs::File, io::Read, path::Path, rc::Rc, time::Duration};
|
||||||
|
|
||||||
|
use cyberpunk::{AsymLine, GlowPen, Pen, Text};
|
||||||
|
use glib::Object;
|
||||||
|
use gtk::{glib, prelude::*, subclass::prelude::*};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
enum TextArea {
|
||||||
|
Top,
|
||||||
|
Middle,
|
||||||
|
Bottom,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct Fade {
|
||||||
|
duration: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
enum AnimationIn {
|
||||||
|
Fade(Fade),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
enum AnimationOut {
|
||||||
|
Fade(Fade),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct ScriptElement {
|
||||||
|
text: String,
|
||||||
|
position: TextArea,
|
||||||
|
transition_in: AnimationIn,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct Script(Vec<ScriptElement>);
|
||||||
|
|
||||||
|
impl Script {
|
||||||
|
fn from_file(path: &Path) -> Result<Script, serde_yml::Error> {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
let mut f = File::open(path).unwrap();
|
||||||
|
f.read_to_end(&mut buf).unwrap();
|
||||||
|
let script = serde_yml::from_slice(&buf)?;
|
||||||
|
Ok(Self(script))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter<'a>(&'a self) -> impl Iterator<Item = &'a ScriptElement> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CyberScreenPrivate {}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for CyberScreenPrivate {
|
||||||
|
const NAME: &'static str = "CyberScreen";
|
||||||
|
type Type = CyberScreen;
|
||||||
|
type ParentType = gtk::DrawingArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for CyberScreenPrivate {}
|
||||||
|
impl WidgetImpl for CyberScreenPrivate {}
|
||||||
|
impl DrawingAreaImpl for CyberScreenPrivate {}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct CyberScreen(ObjectSubclass<CyberScreenPrivate>) @extends gtk::DrawingArea, gtk::Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CyberScreen {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let s: Self = Object::builder().build();
|
||||||
|
|
||||||
|
s.set_draw_func({
|
||||||
|
move |_, context, width, height| {
|
||||||
|
let _ = context.set_source_rgb(0., 0., 0.);
|
||||||
|
let _ = context.paint();
|
||||||
|
|
||||||
|
let pen = GlowPen::new(width, height, 2., 8., (0.7, 0., 1.));
|
||||||
|
|
||||||
|
AsymLine {
|
||||||
|
orientation: gtk::Orientation::Horizontal,
|
||||||
|
start_x: 50.,
|
||||||
|
start_y: height as f64 / 2.,
|
||||||
|
start_length: 75.,
|
||||||
|
height: 50.,
|
||||||
|
total_length: 200.,
|
||||||
|
invert: false,
|
||||||
|
}
|
||||||
|
.draw(&pen);
|
||||||
|
pen.stroke();
|
||||||
|
|
||||||
|
let tracery = pen.finish();
|
||||||
|
let _ = context.set_source(tracery);
|
||||||
|
let _ = context.paint();
|
||||||
|
|
||||||
|
Text::new("Test text".to_owned(), (0.7, 0., 1.)).draw(&context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
/*
|
||||||
|
let script: Script = Script(vec![ScriptElement {
|
||||||
|
text: "abcdefg".to_owned(),
|
||||||
|
position: TextArea::Top,
|
||||||
|
transition_in: AnimationIn::Fade(Fade {
|
||||||
|
duration: Duration::from_secs(10),
|
||||||
|
}),
|
||||||
|
}]);
|
||||||
|
|
||||||
|
println!("{}", serde_yml::to_string(&script).unwrap());
|
||||||
|
*/
|
||||||
|
|
||||||
|
let script = Script::from_file(Path::new("./script.yml")).unwrap();
|
||||||
|
for element in script.iter() {
|
||||||
|
println!("{:?}", element);
|
||||||
|
}
|
||||||
|
|
||||||
|
let app = gtk::Application::builder()
|
||||||
|
.application_id("com.luminescent-dreams.cyberpunk-slideshow")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
app.connect_activate(|app| {
|
||||||
|
let window = gtk::ApplicationWindow::new(app);
|
||||||
|
window.present();
|
||||||
|
|
||||||
|
let screen = CyberScreen::new();
|
||||||
|
|
||||||
|
window.set_child(Some(&screen));
|
||||||
|
window.set_width_request(800);
|
||||||
|
window.set_height_request(600);
|
||||||
|
window.present();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.run();
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ license = "GPL-3.0-only"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cairo-rs = { version = "0.18" }
|
cairo-rs = { version = "0.18" }
|
||||||
|
cyberpunk = { path = "../cyberpunk" }
|
||||||
gio = { version = "0.18" }
|
gio = { version = "0.18" }
|
||||||
glib = { version = "0.18" }
|
glib = { version = "0.18" }
|
||||||
gtk = { version = "0.7", package = "gtk4" }
|
gtk = { version = "0.7", package = "gtk4" }
|
||||||
|
|
|
@ -419,212 +419,6 @@ impl Splash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AsymLineCutout {
|
|
||||||
orientation: gtk::Orientation,
|
|
||||||
start_x: f64,
|
|
||||||
start_y: f64,
|
|
||||||
start_length: f64,
|
|
||||||
total_length: f64,
|
|
||||||
cutout_length: f64,
|
|
||||||
height: f64,
|
|
||||||
invert: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsymLineCutout {
|
|
||||||
fn draw(&self, pen: &impl Pen) {
|
|
||||||
let dodge = if self.invert {
|
|
||||||
self.height
|
|
||||||
} else {
|
|
||||||
-self.height
|
|
||||||
};
|
|
||||||
match self.orientation {
|
|
||||||
gtk::Orientation::Horizontal => {
|
|
||||||
pen.move_to(self.start_x, self.start_y);
|
|
||||||
pen.line_to(self.start_x + self.start_length, self.start_y);
|
|
||||||
pen.line_to(
|
|
||||||
self.start_x + self.start_length + self.height,
|
|
||||||
self.start_y + dodge,
|
|
||||||
);
|
|
||||||
pen.line_to(
|
|
||||||
self.start_x + self.start_length + self.height + self.cutout_length,
|
|
||||||
self.start_y + dodge,
|
|
||||||
);
|
|
||||||
pen.line_to(
|
|
||||||
self.start_x
|
|
||||||
+ self.start_length
|
|
||||||
+ self.height
|
|
||||||
+ self.cutout_length
|
|
||||||
+ (self.height / 2.),
|
|
||||||
self.start_y + dodge / 2.,
|
|
||||||
);
|
|
||||||
pen.line_to(self.total_length, self.start_y + dodge / 2.);
|
|
||||||
}
|
|
||||||
gtk::Orientation::Vertical => {
|
|
||||||
pen.move_to(self.start_x, self.start_y);
|
|
||||||
pen.line_to(self.start_x, self.start_y + self.start_length);
|
|
||||||
pen.line_to(
|
|
||||||
self.start_x + dodge,
|
|
||||||
self.start_y + self.start_length + self.height,
|
|
||||||
);
|
|
||||||
pen.line_to(
|
|
||||||
self.start_x + dodge,
|
|
||||||
self.start_y + self.start_length + self.height + self.cutout_length,
|
|
||||||
);
|
|
||||||
pen.line_to(
|
|
||||||
self.start_x + dodge / 2.,
|
|
||||||
self.start_y
|
|
||||||
+ self.start_length
|
|
||||||
+ self.height
|
|
||||||
+ self.cutout_length
|
|
||||||
+ (self.height / 2.),
|
|
||||||
);
|
|
||||||
pen.line_to(self.start_x + dodge / 2., self.total_length);
|
|
||||||
}
|
|
||||||
_ => panic!("unknown orientation"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AsymLine {
|
|
||||||
orientation: gtk::Orientation,
|
|
||||||
start_x: f64,
|
|
||||||
start_y: f64,
|
|
||||||
start_length: f64,
|
|
||||||
height: f64,
|
|
||||||
total_length: f64,
|
|
||||||
invert: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsymLine {
|
|
||||||
fn draw(&self, pen: &impl Pen) {
|
|
||||||
let dodge = if self.invert {
|
|
||||||
self.height
|
|
||||||
} else {
|
|
||||||
-self.height
|
|
||||||
};
|
|
||||||
match self.orientation {
|
|
||||||
gtk::Orientation::Horizontal => {
|
|
||||||
pen.move_to(self.start_x, self.start_y);
|
|
||||||
pen.line_to(self.start_x + self.start_length, self.start_y);
|
|
||||||
pen.line_to(
|
|
||||||
self.start_x + self.start_length + self.height,
|
|
||||||
self.start_y + dodge,
|
|
||||||
);
|
|
||||||
pen.line_to(self.start_x + self.total_length, self.start_y + dodge);
|
|
||||||
}
|
|
||||||
gtk::Orientation::Vertical => {}
|
|
||||||
_ => panic!("unknown orientation"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SlashMeter {
|
|
||||||
orientation: gtk::Orientation,
|
|
||||||
start_x: f64,
|
|
||||||
start_y: f64,
|
|
||||||
count: u8,
|
|
||||||
fill_count: u8,
|
|
||||||
height: f64,
|
|
||||||
length: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SlashMeter {
|
|
||||||
fn draw(&self, context: &Context) {
|
|
||||||
match self.orientation {
|
|
||||||
gtk::Orientation::Horizontal => {
|
|
||||||
let angle: f64 = 0.8;
|
|
||||||
let run = self.height / angle.tan();
|
|
||||||
let width = self.length / (self.count as f64 * 2.);
|
|
||||||
|
|
||||||
for c in 0..self.count {
|
|
||||||
context.set_line_width(1.);
|
|
||||||
|
|
||||||
let start_x = self.start_x + c as f64 * width * 2.;
|
|
||||||
context.move_to(start_x, self.start_y);
|
|
||||||
context.line_to(start_x + run, self.start_y - self.height);
|
|
||||||
context.line_to(start_x + run + width, self.start_y - self.height);
|
|
||||||
context.line_to(start_x + width, self.start_y);
|
|
||||||
context.line_to(start_x, self.start_y);
|
|
||||||
if c < self.fill_count {
|
|
||||||
let _ = context.fill();
|
|
||||||
} else {
|
|
||||||
let _ = context.stroke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gtk::Orientation::Vertical => {}
|
|
||||||
_ => panic!("unknown orientation"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Pen {
|
|
||||||
fn move_to(&self, x: f64, y: f64);
|
|
||||||
fn line_to(&self, x: f64, y: f64);
|
|
||||||
fn stroke(&self);
|
|
||||||
|
|
||||||
fn finish(self) -> Pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GlowPen {
|
|
||||||
blur_context: Context,
|
|
||||||
draw_context: Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlowPen {
|
|
||||||
fn new(
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
line_width: f64,
|
|
||||||
blur_line_width: f64,
|
|
||||||
color: (f64, f64, f64),
|
|
||||||
) -> Self {
|
|
||||||
let blur_context =
|
|
||||||
Context::new(ImageSurface::create(Format::Rgb24, width, height).unwrap()).unwrap();
|
|
||||||
blur_context.set_line_width(blur_line_width);
|
|
||||||
blur_context.set_source_rgba(color.0, color.1, color.2, 0.5);
|
|
||||||
blur_context.push_group();
|
|
||||||
blur_context.set_line_cap(LineCap::Round);
|
|
||||||
|
|
||||||
let draw_context =
|
|
||||||
Context::new(ImageSurface::create(Format::Rgb24, width, height).unwrap()).unwrap();
|
|
||||||
draw_context.set_line_width(line_width);
|
|
||||||
draw_context.set_source_rgb(color.0, color.1, color.2);
|
|
||||||
draw_context.push_group();
|
|
||||||
draw_context.set_line_cap(LineCap::Round);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
blur_context,
|
|
||||||
draw_context,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pen for GlowPen {
|
|
||||||
fn move_to(&self, x: f64, y: f64) {
|
|
||||||
self.blur_context.move_to(x, y);
|
|
||||||
self.draw_context.move_to(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn line_to(&self, x: f64, y: f64) {
|
|
||||||
self.blur_context.line_to(x, y);
|
|
||||||
self.draw_context.line_to(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stroke(&self) {
|
|
||||||
self.blur_context.stroke().expect("to draw the blur line");
|
|
||||||
self.draw_context
|
|
||||||
.stroke()
|
|
||||||
.expect("to draw the regular line");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(self) -> Pattern {
|
|
||||||
let foreground = self.draw_context.pop_group().unwrap();
|
|
||||||
self.blur_context.set_source(foreground).unwrap();
|
|
||||||
self.blur_context.paint().unwrap();
|
|
||||||
self.blur_context.pop_group().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let app = gtk::Application::builder()
|
let app = gtk::Application::builder()
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "cyberpunk"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cairo-rs = { version = "0.18" }
|
||||||
|
gio = { version = "0.18" }
|
||||||
|
glib = { version = "0.18" }
|
||||||
|
gtk = { version = "0.7", package = "gtk4" }
|
|
@ -0,0 +1,262 @@
|
||||||
|
use cairo::{
|
||||||
|
Context, FontSlant, FontWeight, Format, ImageSurface, LineCap, LinearGradient, Pattern,
|
||||||
|
TextExtents,
|
||||||
|
};
|
||||||
|
use gtk::{prelude::*, subclass::prelude::*, EventControllerKey};
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
rc::Rc,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AsymLineCutout {
|
||||||
|
orientation: gtk::Orientation,
|
||||||
|
start_x: f64,
|
||||||
|
start_y: f64,
|
||||||
|
start_length: f64,
|
||||||
|
total_length: f64,
|
||||||
|
cutout_length: f64,
|
||||||
|
height: f64,
|
||||||
|
invert: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsymLineCutout {
|
||||||
|
fn draw(&self, pen: &impl Pen) {
|
||||||
|
let dodge = if self.invert {
|
||||||
|
self.height
|
||||||
|
} else {
|
||||||
|
-self.height
|
||||||
|
};
|
||||||
|
match self.orientation {
|
||||||
|
gtk::Orientation::Horizontal => {
|
||||||
|
pen.move_to(self.start_x, self.start_y);
|
||||||
|
pen.line_to(self.start_x + self.start_length, self.start_y);
|
||||||
|
pen.line_to(
|
||||||
|
self.start_x + self.start_length + self.height,
|
||||||
|
self.start_y + dodge,
|
||||||
|
);
|
||||||
|
pen.line_to(
|
||||||
|
self.start_x + self.start_length + self.height + self.cutout_length,
|
||||||
|
self.start_y + dodge,
|
||||||
|
);
|
||||||
|
pen.line_to(
|
||||||
|
self.start_x
|
||||||
|
+ self.start_length
|
||||||
|
+ self.height
|
||||||
|
+ self.cutout_length
|
||||||
|
+ (self.height / 2.),
|
||||||
|
self.start_y + dodge / 2.,
|
||||||
|
);
|
||||||
|
pen.line_to(self.total_length, self.start_y + dodge / 2.);
|
||||||
|
}
|
||||||
|
gtk::Orientation::Vertical => {
|
||||||
|
pen.move_to(self.start_x, self.start_y);
|
||||||
|
pen.line_to(self.start_x, self.start_y + self.start_length);
|
||||||
|
pen.line_to(
|
||||||
|
self.start_x + dodge,
|
||||||
|
self.start_y + self.start_length + self.height,
|
||||||
|
);
|
||||||
|
pen.line_to(
|
||||||
|
self.start_x + dodge,
|
||||||
|
self.start_y + self.start_length + self.height + self.cutout_length,
|
||||||
|
);
|
||||||
|
pen.line_to(
|
||||||
|
self.start_x + dodge / 2.,
|
||||||
|
self.start_y
|
||||||
|
+ self.start_length
|
||||||
|
+ self.height
|
||||||
|
+ self.cutout_length
|
||||||
|
+ (self.height / 2.),
|
||||||
|
);
|
||||||
|
pen.line_to(self.start_x + dodge / 2., self.total_length);
|
||||||
|
}
|
||||||
|
_ => panic!("unknown orientation"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents an asymetrical line that starts at one location, then a 45-degree angle and then
|
||||||
|
// another line afterwards.
|
||||||
|
pub struct AsymLine {
|
||||||
|
// Will this be drawn left-to-right or up-to-down?
|
||||||
|
pub orientation: gtk::Orientation,
|
||||||
|
|
||||||
|
// Starting address
|
||||||
|
pub start_x: f64,
|
||||||
|
pub start_y: f64,
|
||||||
|
|
||||||
|
// Length of the first segment
|
||||||
|
pub start_length: f64,
|
||||||
|
|
||||||
|
// Height to dodge over to the next section
|
||||||
|
pub height: f64,
|
||||||
|
|
||||||
|
// Total length of the entire line.
|
||||||
|
pub total_length: f64,
|
||||||
|
|
||||||
|
// When normal, the angle dodge is upwards. When inverted, the angle dodge is downwards.
|
||||||
|
pub invert: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsymLine {
|
||||||
|
pub fn draw(&self, pen: &impl Pen) {
|
||||||
|
let dodge = if self.invert {
|
||||||
|
self.height
|
||||||
|
} else {
|
||||||
|
-self.height
|
||||||
|
};
|
||||||
|
match self.orientation {
|
||||||
|
gtk::Orientation::Horizontal => {
|
||||||
|
pen.move_to(self.start_x, self.start_y);
|
||||||
|
pen.line_to(self.start_x + self.start_length, self.start_y);
|
||||||
|
pen.line_to(
|
||||||
|
self.start_x + self.start_length + self.height,
|
||||||
|
self.start_y + dodge,
|
||||||
|
);
|
||||||
|
pen.line_to(self.start_x + self.total_length, self.start_y + dodge);
|
||||||
|
}
|
||||||
|
gtk::Orientation::Vertical => {}
|
||||||
|
_ => panic!("unknown orientation"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SlashMeter {
|
||||||
|
orientation: gtk::Orientation,
|
||||||
|
start_x: f64,
|
||||||
|
start_y: f64,
|
||||||
|
count: u8,
|
||||||
|
fill_count: u8,
|
||||||
|
height: f64,
|
||||||
|
length: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlashMeter {
|
||||||
|
fn draw(&self, context: &Context) {
|
||||||
|
match self.orientation {
|
||||||
|
gtk::Orientation::Horizontal => {
|
||||||
|
let angle: f64 = 0.8;
|
||||||
|
let run = self.height / angle.tan();
|
||||||
|
let width = self.length / (self.count as f64 * 2.);
|
||||||
|
|
||||||
|
for c in 0..self.count {
|
||||||
|
context.set_line_width(1.);
|
||||||
|
|
||||||
|
let start_x = self.start_x + c as f64 * width * 2.;
|
||||||
|
context.move_to(start_x, self.start_y);
|
||||||
|
context.line_to(start_x + run, self.start_y - self.height);
|
||||||
|
context.line_to(start_x + run + width, self.start_y - self.height);
|
||||||
|
context.line_to(start_x + width, self.start_y);
|
||||||
|
context.line_to(start_x, self.start_y);
|
||||||
|
if c < self.fill_count {
|
||||||
|
let _ = context.fill();
|
||||||
|
} else {
|
||||||
|
let _ = context.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gtk::Orientation::Vertical => {}
|
||||||
|
_ => panic!("unknown orientation"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a pen for drawing a pattern. This is good for complex patterns that may require
|
||||||
|
/// multiple identical steps.
|
||||||
|
pub trait Pen {
|
||||||
|
/// Move the pen to a location.
|
||||||
|
fn move_to(&self, x: f64, y: f64);
|
||||||
|
|
||||||
|
/// Draw a line from the current location to the specified destination.
|
||||||
|
fn line_to(&self, x: f64, y: f64);
|
||||||
|
|
||||||
|
/// Instantiate the line.
|
||||||
|
fn stroke(&self);
|
||||||
|
|
||||||
|
/// Convert all of the drawing into a pattern that can be painted to a drawing context.
|
||||||
|
fn finish(self) -> Pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GlowPen {
|
||||||
|
blur_context: Context,
|
||||||
|
draw_context: Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlowPen {
|
||||||
|
pub fn new(
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
line_width: f64,
|
||||||
|
blur_line_width: f64,
|
||||||
|
color: (f64, f64, f64),
|
||||||
|
) -> Self {
|
||||||
|
let blur_context =
|
||||||
|
Context::new(ImageSurface::create(Format::Rgb24, width, height).unwrap()).unwrap();
|
||||||
|
blur_context.set_line_width(blur_line_width);
|
||||||
|
blur_context.set_source_rgba(color.0, color.1, color.2, 0.5);
|
||||||
|
blur_context.push_group();
|
||||||
|
blur_context.set_line_cap(LineCap::Round);
|
||||||
|
|
||||||
|
let draw_context =
|
||||||
|
Context::new(ImageSurface::create(Format::Rgb24, width, height).unwrap()).unwrap();
|
||||||
|
draw_context.set_line_width(line_width);
|
||||||
|
draw_context.set_source_rgb(color.0, color.1, color.2);
|
||||||
|
draw_context.push_group();
|
||||||
|
draw_context.set_line_cap(LineCap::Round);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
blur_context,
|
||||||
|
draw_context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pen for GlowPen {
|
||||||
|
fn move_to(&self, x: f64, y: f64) {
|
||||||
|
self.blur_context.move_to(x, y);
|
||||||
|
self.draw_context.move_to(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line_to(&self, x: f64, y: f64) {
|
||||||
|
self.blur_context.line_to(x, y);
|
||||||
|
self.draw_context.line_to(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stroke(&self) {
|
||||||
|
self.blur_context.stroke().expect("to draw the blur line");
|
||||||
|
self.draw_context
|
||||||
|
.stroke()
|
||||||
|
.expect("to draw the regular line");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self) -> Pattern {
|
||||||
|
let foreground = self.draw_context.pop_group().unwrap();
|
||||||
|
self.blur_context.set_source(foreground).unwrap();
|
||||||
|
self.blur_context.paint().unwrap();
|
||||||
|
self.blur_context.pop_group().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Text {
|
||||||
|
content: String,
|
||||||
|
color: (f64, f64, f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Text {
|
||||||
|
pub fn new(content: String, color: (f64, f64, f64)) -> Self {
|
||||||
|
|
||||||
|
Self{ content, color }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self, context: &Context) {
|
||||||
|
context.select_font_face("Alegreya Sans SC", FontSlant::Normal, FontWeight::Bold);
|
||||||
|
context.set_font_size(64.);
|
||||||
|
|
||||||
|
let extents = context.text_extents(&self.content).unwrap();
|
||||||
|
context.move_to(20., extents.height() + 20.);
|
||||||
|
|
||||||
|
context.set_source_rgb(self.color.0, self.color.1, self.color.2);
|
||||||
|
let _ = context.show_text(&self.content);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue