Compare commits
6 Commits
b9f1424b54
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fc5ff2cf61 | |||
| 0abb4c2096 | |||
| 22ef359ed4 | |||
| 392cea7a6a | |||
| 092c44e61e | |||
| 1acaabb1c2 |
19
.direnv/bin/nix-direnv-reload
Executable file
19
.direnv/bin/nix-direnv-reload
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
if [[ ! -d "/home/eesim/Projects/waywrite" ]]; then
|
||||||
|
echo "Cannot find source directory; Did you move it?"
|
||||||
|
echo "(Looking for "/home/eesim/Projects/waywrite")"
|
||||||
|
echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# rebuild the cache forcefully
|
||||||
|
_nix_direnv_force_reload=1 direnv exec "/home/eesim/Projects/waywrite" true
|
||||||
|
|
||||||
|
# Update the mtime for .envrc.
|
||||||
|
# This will cause direnv to reload again - but without re-building.
|
||||||
|
touch "/home/eesim/Projects/waywrite/.envrc"
|
||||||
|
|
||||||
|
# Also update the timestamp of whatever profile_rc we have.
|
||||||
|
# This makes sure that we know we are up to date.
|
||||||
|
touch -r "/home/eesim/Projects/waywrite/.envrc" "/home/eesim/Projects/waywrite/.direnv"/*.rc
|
||||||
1
.direnv/nix-profile-24.11-iqxdbcjlg9wx8gdc
Symbolic link
1
.direnv/nix-profile-24.11-iqxdbcjlg9wx8gdc
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/nix/store/3aamj0h5bv74fnn44584y5yp480076xk-nix-shell-env
|
||||||
1988
.direnv/nix-profile-24.11-iqxdbcjlg9wx8gdc.rc
Normal file
1988
.direnv/nix-profile-24.11-iqxdbcjlg9wx8gdc.rc
Normal file
File diff suppressed because it is too large
Load Diff
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
*.rten
|
*.rten
|
||||||
download-models.sh
|
download-models.sh
|
||||||
|
*.png
|
||||||
|
|||||||
1059
Cargo.lock
generated
1059
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,13 +6,13 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
relm4 = "0.6.*"
|
rten-tensor = "0.8.0"
|
||||||
rten-tensor = "0.3.*"
|
rten = "0.8.0"
|
||||||
rten = "0.3.1"
|
ocrs = "0.6.0"
|
||||||
ocrs = "0.4.*"
|
|
||||||
image = "0.24.9"
|
image = "0.24.9"
|
||||||
anyhow = "1.0.81"
|
anyhow = "1.0.81"
|
||||||
smithay-client-toolkit = "0.18.1"
|
smithay-client-toolkit = "0.18.1"
|
||||||
wayland-client = "0.31.2"
|
wayland-client = "0.31.2"
|
||||||
itertools = "0.12.1"
|
itertools = "0.12.1"
|
||||||
raqote = "0.8.4"
|
raqote = "0.8.4"
|
||||||
|
wayland-protocols-misc = {version = "0.2.0", features = ["client"]}
|
||||||
|
|||||||
4
rust-toolchain.toml
Normal file
4
rust-toolchain.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "stable"
|
||||||
|
components = [ "rust-analyzer" ]
|
||||||
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
41
shell.nix
Normal file
41
shell.nix
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
let
|
||||||
|
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
|
||||||
|
libPath = with pkgs; lib.makeLibraryPath [
|
||||||
|
];
|
||||||
|
in
|
||||||
|
pkgs.mkShell rec {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
clang
|
||||||
|
llvmPackages.bintools
|
||||||
|
rustup
|
||||||
|
pkg-config
|
||||||
|
fontconfig
|
||||||
|
libxkbcommon
|
||||||
|
];
|
||||||
|
RUSTC_VERSION = overrides.toolchain.channel;
|
||||||
|
# https://github.com/rust-lang/rust-bindgen#environment-variables
|
||||||
|
LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
|
||||||
|
shellHook = ''
|
||||||
|
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
||||||
|
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
|
||||||
|
'';
|
||||||
|
# Add precompiled library to rustc search path
|
||||||
|
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
|
||||||
|
# add libraries here (e.g. pkgs.libvmi)
|
||||||
|
]);
|
||||||
|
LD_LIBRARY_PATH = libPath;
|
||||||
|
# Add glibc, clang, glib, and other headers to bindgen search path
|
||||||
|
BINDGEN_EXTRA_CLANG_ARGS =
|
||||||
|
# Includes normal include path
|
||||||
|
(builtins.map (a: ''-I"${a}/include"'') [
|
||||||
|
# add dev libraries here (e.g. pkgs.libvmi.dev)
|
||||||
|
pkgs.glibc.dev
|
||||||
|
])
|
||||||
|
# Includes with special directory paths
|
||||||
|
++ [
|
||||||
|
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
||||||
|
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
||||||
|
''-I${pkgs.glib.out}/lib/glib-2.0/include/''
|
||||||
|
];
|
||||||
|
}
|
||||||
158
src/main.rs
158
src/main.rs
@@ -1,162 +1,12 @@
|
|||||||
use relm4::prelude::{gtk, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
|
use anyhow;
|
||||||
use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, WidgetExt, GestureSingleExt};
|
|
||||||
use gtk::cairo::{Context, Operator};
|
|
||||||
use relm4::drawing::DrawHandler;
|
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
|
|
||||||
mod wayland;
|
mod wayland;
|
||||||
mod process_point;
|
mod process_point;
|
||||||
|
|
||||||
use process_point::{Point, print_words};
|
use wayland::app::SimpleWindow;
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn main() -> anyhow::Result<()> {
|
||||||
enum AppInput {
|
|
||||||
Input,
|
|
||||||
NewLine((f64, f64)),
|
|
||||||
AddPoint((f64, f64)),
|
|
||||||
Reset,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
SimpleWindow::run()?;
|
||||||
#[derive(Debug)]
|
|
||||||
struct AppModel {
|
|
||||||
counter: u8,
|
|
||||||
points: Vec<Point>,
|
|
||||||
handler: DrawHandler,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct UpdatePoints {}
|
|
||||||
|
|
||||||
#[relm4::component]
|
|
||||||
impl SimpleComponent for AppModel {
|
|
||||||
type Init = u8;
|
|
||||||
type Input = AppInput;
|
|
||||||
type Output = UpdatePoints;
|
|
||||||
|
|
||||||
view! {
|
|
||||||
gtk::Window {
|
|
||||||
set_title: Some("Waywrite"),
|
|
||||||
set_default_width: 600,
|
|
||||||
set_default_height: 200,
|
|
||||||
|
|
||||||
gtk::Box{
|
|
||||||
set_orientation: gtk::Orientation::Horizontal,
|
|
||||||
set_spacing: 5,
|
|
||||||
set_margin_all: 2,
|
|
||||||
set_hexpand: true,
|
|
||||||
|
|
||||||
gtk::Box {
|
|
||||||
set_orientation: gtk::Orientation::Vertical,
|
|
||||||
set_spacing: 5,
|
|
||||||
set_margin_all: 5,
|
|
||||||
set_vexpand: true,
|
|
||||||
|
|
||||||
#[local_ref]
|
|
||||||
area -> gtk::DrawingArea {
|
|
||||||
set_vexpand: true,
|
|
||||||
set_hexpand: true,
|
|
||||||
|
|
||||||
add_controller = gtk::GestureStylus {
|
|
||||||
set_button: 0,
|
|
||||||
connect_motion[sender] => move |_, x, y| {
|
|
||||||
sender.input(AppInput::AddPoint((x, y)));
|
|
||||||
},
|
|
||||||
connect_down[sender] => move |_, x, y| {
|
|
||||||
sender.input(AppInput::NewLine((x, y)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
gtk::Box {
|
|
||||||
set_orientation: gtk::Orientation::Vertical,
|
|
||||||
set_spacing: 4,
|
|
||||||
set_margin_all: 2,
|
|
||||||
set_halign: gtk::Align::End,
|
|
||||||
|
|
||||||
gtk::Button {
|
|
||||||
set_label: "Enter",
|
|
||||||
connect_clicked[sender] => move |_| {
|
|
||||||
sender.input(AppInput::Input);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
gtk::Button {
|
|
||||||
set_label: "Erase",
|
|
||||||
connect_clicked[sender] => move |_| {
|
|
||||||
sender.input(AppInput::Reset);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(
|
|
||||||
_: Self::Init,
|
|
||||||
root: &Self::Root,
|
|
||||||
sender: ComponentSender<Self>,
|
|
||||||
) ->relm4::ComponentParts<Self> {
|
|
||||||
let model = AppModel {
|
|
||||||
counter: 0,
|
|
||||||
points: Vec::new(),
|
|
||||||
handler: DrawHandler::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let area = model.handler.drawing_area();
|
|
||||||
let widgets = view_output!();
|
|
||||||
|
|
||||||
ComponentParts { model, widgets }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
|
|
||||||
let cx = self.handler.get_context();
|
|
||||||
|
|
||||||
match message {
|
|
||||||
AppInput::Input => {
|
|
||||||
print_words(&self.points).unwrap();
|
|
||||||
}
|
|
||||||
AppInput::AddPoint((x, y)) => {
|
|
||||||
self.points.push(Point { x, y, new_line: false })
|
|
||||||
}
|
|
||||||
AppInput::NewLine((x, y)) => {
|
|
||||||
self.points.push(Point { x, y, new_line: true })
|
|
||||||
}
|
|
||||||
AppInput::Reset => {
|
|
||||||
cx.set_operator(Operator::Clear);
|
|
||||||
cx.set_source_rgba(0.0, 0.0, 0.0, 0.0);
|
|
||||||
cx.paint().expect("Could not fill context");
|
|
||||||
self.points = Vec::new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
draw(&cx, &self.points);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(cx: &Context, points: &[Point]) {
|
|
||||||
for (i, point) in points.into_iter().enumerate().filter(|(i, _)| *i != 0) {
|
|
||||||
|
|
||||||
if !point.new_line {
|
|
||||||
let last_point = &points[i - 1];
|
|
||||||
|
|
||||||
cx.move_to(last_point.x as f64, last_point.y as f64);
|
|
||||||
cx.line_to(point.x as f64, point.y as f64);
|
|
||||||
cx.set_source_rgb(200.0, 200.0, 200.0);
|
|
||||||
cx.set_line_width(2.0);
|
|
||||||
cx.stroke().expect("Failed to draw line");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
|
|
||||||
wayland::run()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,22 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::error::Error;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use rten_tensor::{NdTensor, AsView};
|
use rten_tensor::{NdTensor, AsView};
|
||||||
use rten::Model;
|
use rten::Model;
|
||||||
use ocrs::{OcrEngine, OcrEngineParams};
|
use ocrs::{OcrEngine, OcrEngineParams};
|
||||||
use image::{ColorType, ImageFormat};
|
|
||||||
|
|
||||||
const MATRIX_LEN: usize = 800;
|
pub fn print_words(image: NdTensor<f32, 3>) -> anyhow::Result<()> {
|
||||||
const TEXT_MATRIX_RATIO: f64 = 0.5;
|
|
||||||
const LINE_WIDTH: f64 = 10.0;
|
|
||||||
|
|
||||||
const MATRIX_X_SIZE: f64 = MATRIX_LEN as f64;
|
|
||||||
const TEXT_X_SIZE: f64 = (MATRIX_X_SIZE as f64) * TEXT_MATRIX_RATIO;
|
|
||||||
const TEXT_X_OFFSET: f64 = (MATRIX_X_SIZE - TEXT_X_SIZE) / 2.0;
|
|
||||||
const LINE_WIDTH_X_OFFSET: f64 = LINE_WIDTH / 2.0;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Point {
|
|
||||||
pub x: f64,
|
|
||||||
pub y: f64,
|
|
||||||
pub new_line: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_words(points: &Vec<Point>) -> Result<(), Box<dyn Error>> {
|
|
||||||
|
|
||||||
let begin = Instant::now();
|
let begin = Instant::now();
|
||||||
let processed_data = process(points);
|
|
||||||
|
|
||||||
println!("{:#?}", begin.elapsed());
|
ocr(image).unwrap();
|
||||||
let begin = Instant::now();
|
|
||||||
ocr(processed_data)?;
|
|
||||||
println!("{:#?}", begin.elapsed());
|
println!("{:#?}", begin.elapsed());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(points: &Vec<Point>) -> NdTensor<f32, 3> {
|
fn ocr(data: NdTensor<f32, 3>) -> anyhow::Result<()> {
|
||||||
|
|
||||||
let matrix = to_matrix(points);
|
|
||||||
let y_len = matrix[0].len();
|
|
||||||
let x_len = matrix[0][0].len();
|
|
||||||
let image_data: Box<[u8]> = matrix.iter().flatten().flatten().map(|f| if *f > 0.5 { u8::from(0) } else { u8::from(255) }).collect();
|
|
||||||
let data: Vec<f32> = matrix.into_iter().flatten().flatten().map(|f| f as f32).collect();
|
|
||||||
|
|
||||||
image::save_buffer_with_format("./image.png", &image_data, x_len as u32, y_len as u32, ColorType::L8, ImageFormat::Png).unwrap();
|
|
||||||
|
|
||||||
|
|
||||||
NdTensor::from_data([1, y_len, x_len], data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ocr(data: NdTensor<f32, 3>) -> Result<(), Box<dyn Error>> {
|
|
||||||
|
|
||||||
let detection_model_data = fs::read("text-detection.rten")?;
|
let detection_model_data = fs::read("text-detection.rten")?;
|
||||||
let rec_model_data = fs::read("text-recognition.rten")?;
|
let rec_model_data = fs::read("text-recognition.rten")?;
|
||||||
@@ -58,7 +24,6 @@ fn ocr(data: NdTensor<f32, 3>) -> Result<(), Box<dyn Error>> {
|
|||||||
let detection_model = Model::load(&detection_model_data)?;
|
let detection_model = Model::load(&detection_model_data)?;
|
||||||
let rec_model = Model::load(&rec_model_data)?;
|
let rec_model = Model::load(&rec_model_data)?;
|
||||||
|
|
||||||
|
|
||||||
let ocr_engine = OcrEngine::new(OcrEngineParams {
|
let ocr_engine = OcrEngine::new(OcrEngineParams {
|
||||||
detection_model: Some(detection_model),
|
detection_model: Some(detection_model),
|
||||||
recognition_model: Some(rec_model),
|
recognition_model: Some(rec_model),
|
||||||
@@ -87,91 +52,3 @@ fn ocr(data: NdTensor<f32, 3>) -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line(x: f64, point1: (f64, f64), point2: (f64, f64)) -> f64 {
|
|
||||||
let slope = (point2.1 - point1.1) / (point2.0 - point1.0);
|
|
||||||
|
|
||||||
let point = slope * (x - point1.0) + point1.1;
|
|
||||||
|
|
||||||
point
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_matrix(points: &Vec<Point>) -> Vec<Vec<Vec<f64>>> {
|
|
||||||
|
|
||||||
let min_x = points.iter().min_by_key(|p| p.x as i32).unwrap().x;
|
|
||||||
let min_y = points.iter().min_by_key(|p| p.y as i32).unwrap().y;
|
|
||||||
let max_x = points.iter().max_by_key(|p| p.x as i32).unwrap().x;
|
|
||||||
let max_y = points.iter().max_by_key(|p| p.y as i32).unwrap().y;
|
|
||||||
|
|
||||||
let x_len = max_x - min_x;
|
|
||||||
let y_len = max_y - min_y;
|
|
||||||
|
|
||||||
let y_ratio = y_len / x_len;
|
|
||||||
|
|
||||||
let matrix_y_size = MATRIX_X_SIZE * y_ratio;
|
|
||||||
let text_y_size = TEXT_X_SIZE * y_ratio;
|
|
||||||
let text_y_offset = TEXT_X_OFFSET * y_ratio;
|
|
||||||
let line_width_y_offset = LINE_WIDTH_X_OFFSET * y_ratio;
|
|
||||||
|
|
||||||
let x_scale = MATRIX_X_SIZE / x_len;
|
|
||||||
let y_scale = matrix_y_size / y_len;
|
|
||||||
|
|
||||||
|
|
||||||
let mut matrix: Vec<Vec<f64>> = vec![
|
|
||||||
vec![0.0; MATRIX_LEN]; (matrix_y_size as usize) + 1
|
|
||||||
];
|
|
||||||
|
|
||||||
let scaled_points: Vec<((f64, f64), bool)> = points
|
|
||||||
.iter()
|
|
||||||
.map(|point| {
|
|
||||||
let x_scaled = ((point.x - min_x) * x_scale) + TEXT_X_OFFSET;
|
|
||||||
let y_scaled = ((point.y - min_y) * y_scale) + text_y_offset;
|
|
||||||
|
|
||||||
((x_scaled, y_scaled), point.new_line)
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let mut last_x = 0.0;
|
|
||||||
let mut last_y = 0.0;
|
|
||||||
|
|
||||||
for ((current_x, current_y), newline) in scaled_points {
|
|
||||||
|
|
||||||
if !newline {
|
|
||||||
let curr_x_start = current_x - LINE_WIDTH_X_OFFSET;
|
|
||||||
let curr_x_end = current_x + LINE_WIDTH_X_OFFSET;
|
|
||||||
|
|
||||||
let last_x_start = last_x - LINE_WIDTH_X_OFFSET;
|
|
||||||
let last_x_end = last_x + LINE_WIDTH_X_OFFSET;
|
|
||||||
|
|
||||||
let top_y = current_y.max(last_y) + line_width_y_offset;
|
|
||||||
let bottom_y = current_y.min(last_y) - line_width_y_offset;
|
|
||||||
|
|
||||||
let start_x = (last_x_start.min(curr_x_start)) as usize;
|
|
||||||
let end_x = (last_x_end.max(curr_x_end)) as usize + 1;
|
|
||||||
|
|
||||||
|
|
||||||
for x in start_x..(end_x + 1) {
|
|
||||||
|
|
||||||
let left_line_y = line(x as f64, (last_x_start, last_y), (curr_x_start, current_y));
|
|
||||||
let right_line_y = line(x as f64, (last_x_end, last_y), (curr_x_end, current_x));
|
|
||||||
|
|
||||||
let top_line_y = left_line_y
|
|
||||||
.max(right_line_y)
|
|
||||||
.min(top_y) as usize;
|
|
||||||
let bottom_line_y = left_line_y
|
|
||||||
.min(right_line_y)
|
|
||||||
.max(bottom_y) as usize;
|
|
||||||
|
|
||||||
for y in bottom_line_y..(top_line_y + 1) {
|
|
||||||
matrix[y][x] = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
last_x = current_x;
|
|
||||||
last_y = current_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec![matrix]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
411
src/wayland.rs
411
src/wayland.rs
@@ -1,409 +1,18 @@
|
|||||||
use smithay_client_toolkit::reexports::calloop::{EventLoop, LoopHandle}; use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource;
|
pub mod app;
|
||||||
use smithay_client_toolkit::{
|
pub mod draw;
|
||||||
activation::{ActivationState, ActivationHandler, RequestData},
|
|
||||||
compositor::{CompositorState, CompositorHandler},
|
|
||||||
output::{OutputHandler, OutputState},
|
|
||||||
registry::{RegistryState, ProvidesRegistryState},
|
|
||||||
registry_handlers,
|
|
||||||
seat::{
|
|
||||||
pointer::{PointerEvent, PointerEventKind, PointerHandler},
|
|
||||||
SeatState, SeatHandler, Capability
|
|
||||||
},
|
|
||||||
shell::{
|
|
||||||
xdg::{
|
|
||||||
window::{WindowHandler, Window, WindowConfigure, WindowDecorations},
|
|
||||||
XdgShell,
|
|
||||||
},
|
|
||||||
WaylandSurface,
|
|
||||||
},
|
|
||||||
shm::{ slot::{SlotPool, Buffer},
|
|
||||||
Shm, ShmHandler
|
|
||||||
},
|
|
||||||
delegate_registry, delegate_compositor, delegate_seat, delegate_output,
|
|
||||||
delegate_xdg_shell, delegate_shm, delegate_activation, delegate_xdg_window,
|
|
||||||
delegate_pointer,
|
|
||||||
};
|
|
||||||
|
|
||||||
use wayland_client::{
|
|
||||||
Connection, QueueHandle,
|
|
||||||
globals::registry_queue_init,
|
|
||||||
protocol::{wl_surface, wl_output, wl_seat, wl_shm, wl_pointer},
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
mod app;
|
mod builder;
|
||||||
|
|
||||||
use ui::{ButtonBuilder, DrawAreaBuilder, TextAreaBuilder, DrawPath, Position};
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub(super) struct Position {
|
||||||
|
pub(super) x: f32,
|
||||||
const WINDOW_HEIGHT: u32 = 256;
|
pub(super) y: f32,
|
||||||
const WINDOW_WIDTH: u32 = 512;
|
|
||||||
|
|
||||||
|
|
||||||
pub(super) fn run() -> Result<()> {
|
|
||||||
let conn = Connection::connect_to_env()?;
|
|
||||||
|
|
||||||
let (globals, event_queue) = registry_queue_init(&conn)?;
|
|
||||||
let qh = event_queue.handle();
|
|
||||||
let mut event_loop: EventLoop::<SimpleWindow> =
|
|
||||||
EventLoop::try_new()?;
|
|
||||||
let loop_handle = event_loop.handle();
|
|
||||||
WaylandSource::new(conn.clone(), event_queue).insert(loop_handle)?;
|
|
||||||
|
|
||||||
let compositor = CompositorState::bind(&globals, &qh)?;
|
|
||||||
let xdg_shell = XdgShell::bind(&globals, &qh)?;
|
|
||||||
let shm = Shm::bind(&globals, &qh)?;
|
|
||||||
let xdg_activation = ActivationState::bind(&globals, &qh).ok();
|
|
||||||
|
|
||||||
let surface = compositor.create_surface(&qh);
|
|
||||||
let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
|
|
||||||
|
|
||||||
window.set_title("A window");
|
|
||||||
window.set_app_id("simmer.simplewindow");
|
|
||||||
window.set_min_size(Some((WINDOW_WIDTH, WINDOW_HEIGHT)));
|
|
||||||
window.set_max_size(Some((WINDOW_WIDTH, WINDOW_HEIGHT)));
|
|
||||||
|
|
||||||
window.commit();
|
|
||||||
|
|
||||||
if let Some(activation) = xdg_activation.as_ref() {
|
|
||||||
activation.request_token(
|
|
||||||
&qh,
|
|
||||||
RequestData {
|
|
||||||
seat_and_serial: None,
|
|
||||||
surface: Some(window.wl_surface().clone()),
|
|
||||||
app_id: Some(String::from("simmer.waywrite")),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let pool = SlotPool::new((WINDOW_WIDTH as usize) * (WINDOW_HEIGHT as usize) * 4, &shm)?;
|
|
||||||
|
|
||||||
let mut simple_window = SimpleWindow {
|
|
||||||
registry_state: RegistryState::new(&globals),
|
|
||||||
seat_state: SeatState::new(&globals, &qh),
|
|
||||||
output_state: OutputState::new(&globals, &qh),
|
|
||||||
xdg_activation,
|
|
||||||
|
|
||||||
shm,
|
|
||||||
buffer: None,
|
|
||||||
pool,
|
|
||||||
window,
|
|
||||||
width: WINDOW_WIDTH,
|
|
||||||
height: WINDOW_HEIGHT,
|
|
||||||
cursor_down: false,
|
|
||||||
exit: false,
|
|
||||||
first_configure: true,
|
|
||||||
pointer: None,
|
|
||||||
_loop_handle: event_loop.handle(),
|
|
||||||
|
|
||||||
draw_path: DrawPath::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
event_loop.dispatch(Duration::from_millis(16), &mut simple_window)?;
|
|
||||||
|
|
||||||
if simple_window.exit {
|
|
||||||
println!("exiting");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SimpleWindow {
|
impl Position {
|
||||||
registry_state: RegistryState,
|
pub(super) fn new(x: f32, y: f32) -> Self {
|
||||||
seat_state: SeatState,
|
Position { x, y }
|
||||||
output_state: OutputState,
|
|
||||||
shm: Shm,
|
|
||||||
xdg_activation: Option<ActivationState>,
|
|
||||||
|
|
||||||
buffer: Option<Buffer>,
|
|
||||||
pool: SlotPool,
|
|
||||||
window: Window,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
cursor_down: bool,
|
|
||||||
exit: bool,
|
|
||||||
first_configure: bool,
|
|
||||||
pointer: Option<wl_pointer::WlPointer>,
|
|
||||||
_loop_handle: LoopHandle<'static, SimpleWindow>,
|
|
||||||
|
|
||||||
draw_path: DrawPath,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompositorHandler for SimpleWindow {
|
|
||||||
fn scale_factor_changed(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: &wl_surface::WlSurface,
|
|
||||||
_: i32) {}
|
|
||||||
fn transform_changed(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: &wl_surface::WlSurface,
|
|
||||||
_: wl_output::Transform) {}
|
|
||||||
fn frame(
|
|
||||||
&mut self,
|
|
||||||
conn: &Connection,
|
|
||||||
qh: &QueueHandle<Self>,
|
|
||||||
_: &wl_surface::WlSurface,
|
|
||||||
_: u32
|
|
||||||
) {
|
|
||||||
self.draw(conn, qh);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SeatHandler for SimpleWindow {
|
|
||||||
fn seat_state(&mut self) -> &mut SeatState {
|
|
||||||
&mut self.seat_state
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_seat(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: wl_seat::WlSeat
|
|
||||||
) {}
|
|
||||||
fn new_capability(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
qh: &QueueHandle<Self>,
|
|
||||||
seat: wl_seat::WlSeat,
|
|
||||||
capability: Capability,
|
|
||||||
) {
|
|
||||||
if capability == Capability::Pointer && self.pointer.is_none() {
|
|
||||||
println!("Set pointer capability");
|
|
||||||
let pointer = self.seat_state.get_pointer(qh, &seat).expect("Failed to create pointer");
|
|
||||||
self.pointer = Some(pointer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn remove_capability(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: wl_seat::WlSeat,
|
|
||||||
capability: Capability,
|
|
||||||
) {
|
|
||||||
if capability == Capability::Pointer && self.pointer.is_some() {
|
|
||||||
println!("Unset pointer capability");
|
|
||||||
self.pointer.take().unwrap().release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn remove_seat(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: wl_seat::WlSeat,
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputHandler for SimpleWindow {
|
|
||||||
fn output_state(&mut self) -> &mut OutputState {
|
|
||||||
&mut self.output_state
|
|
||||||
}
|
|
||||||
fn new_output(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: wl_output::WlOutput,
|
|
||||||
) {}
|
|
||||||
fn update_output(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: wl_output::WlOutput,
|
|
||||||
) {}
|
|
||||||
fn output_destroyed(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: wl_output::WlOutput,
|
|
||||||
) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowHandler for SimpleWindow {
|
|
||||||
fn request_close(
|
|
||||||
&mut self,
|
|
||||||
_: &Connection,
|
|
||||||
_: &QueueHandle<Self>,
|
|
||||||
_: &Window
|
|
||||||
) {
|
|
||||||
self.exit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure(
|
|
||||||
&mut self,
|
|
||||||
conn: &Connection,
|
|
||||||
qh: &QueueHandle<Self>,
|
|
||||||
_: &Window,
|
|
||||||
_: WindowConfigure,
|
|
||||||
_: u32,
|
|
||||||
) {
|
|
||||||
if self.first_configure {
|
|
||||||
self.first_configure = false;
|
|
||||||
self.draw(conn, qh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ShmHandler for SimpleWindow {
|
|
||||||
fn shm_state(&mut self) -> &mut Shm {
|
|
||||||
&mut self.shm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActivationHandler for SimpleWindow {
|
|
||||||
type RequestData = RequestData;
|
|
||||||
|
|
||||||
fn new_token(&mut self, token: String, _: &Self::RequestData) {
|
|
||||||
self.xdg_activation
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.activate::<SimpleWindow>(self.window.wl_surface(), token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PointerHandler for SimpleWindow {
|
|
||||||
fn pointer_frame(
|
|
||||||
&mut self,
|
|
||||||
_conn: &Connection,
|
|
||||||
_qh: &QueueHandle<Self>,
|
|
||||||
_pointer: &wl_pointer::WlPointer,
|
|
||||||
events: &[PointerEvent],
|
|
||||||
) {
|
|
||||||
use PointerEventKind::*;
|
|
||||||
for event in events {
|
|
||||||
// Ignore events for other surfaces
|
|
||||||
if &event.surface != self.window.wl_surface() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match event.kind {
|
|
||||||
Enter { .. } => {
|
|
||||||
println!("Pointer entered @{:?}", event.position);
|
|
||||||
}
|
|
||||||
Leave { .. } => {
|
|
||||||
println!("Pointer left");
|
|
||||||
}
|
|
||||||
Motion { .. } => {
|
|
||||||
if self.cursor_down {
|
|
||||||
self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Press { button, .. } => {
|
|
||||||
println!("Press {:x} @ {:?}", button, event.position);
|
|
||||||
self.cursor_down = true;
|
|
||||||
self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), true)
|
|
||||||
}
|
|
||||||
Release { button, .. } => {
|
|
||||||
println!("Release {:x} @ {:?}", button, event.position);
|
|
||||||
self.cursor_down = false;
|
|
||||||
self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), false)
|
|
||||||
}
|
|
||||||
Axis { horizontal, vertical, .. } => {
|
|
||||||
println!("Scroll H:{horizontal:?}, V:{vertical:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SimpleWindow {
|
|
||||||
fn draw(&mut self, _: &Connection, qh: &QueueHandle<Self>) {
|
|
||||||
let buffer = self.buffer.get_or_insert_with(|| {
|
|
||||||
self.pool
|
|
||||||
.create_buffer(self.width as i32, self.height as i32, (self.width as i32) * 4, wl_shm::Format::Argb8888)
|
|
||||||
.expect("create buffer")
|
|
||||||
.0
|
|
||||||
});
|
|
||||||
|
|
||||||
let canvas = match self.pool.canvas(buffer) {
|
|
||||||
Some(canvas) => canvas,
|
|
||||||
None => {
|
|
||||||
let (second_buffer, canvas) = self
|
|
||||||
.pool
|
|
||||||
.create_buffer(
|
|
||||||
self.width as i32,
|
|
||||||
self.height as i32,
|
|
||||||
(self.width as i32) * 4,
|
|
||||||
wl_shm::Format::Argb8888,
|
|
||||||
)
|
|
||||||
.expect("create buffer");
|
|
||||||
*buffer = second_buffer;
|
|
||||||
canvas
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let draw_buffer: &mut [u32] = unsafe {canvas.align_to_mut::<u32>().1};
|
|
||||||
|
|
||||||
|
|
||||||
let mut window = ui::Window::new(
|
|
||||||
draw_buffer,
|
|
||||||
self.width.try_into().unwrap(),
|
|
||||||
self.height.try_into().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut button = ButtonBuilder::new();
|
|
||||||
let mut draw_area = DrawAreaBuilder::new();
|
|
||||||
|
|
||||||
button.position(self.width as f32 - 100.0, 50.0);
|
|
||||||
button.width(100.0);
|
|
||||||
button.height(50.0);
|
|
||||||
let button = button.finish();
|
|
||||||
|
|
||||||
draw_area.position(0.0, 50.0);
|
|
||||||
draw_area.width(self.width as f32 - 100.0);
|
|
||||||
draw_area.height(self.height as f32 - 50.0);
|
|
||||||
draw_area.path(&self.draw_path);
|
|
||||||
let draw_area = draw_area.finish();
|
|
||||||
|
|
||||||
|
|
||||||
let mut text_area = TextAreaBuilder::new();
|
|
||||||
text_area.position(0.0, 0.0);
|
|
||||||
text_area.width(self.width as f32);
|
|
||||||
text_area.height(50.0);
|
|
||||||
let text_area = text_area.finish();
|
|
||||||
|
|
||||||
window.add_widget(button);
|
|
||||||
window.add_widget(draw_area);
|
|
||||||
window.add_widget(text_area);
|
|
||||||
|
|
||||||
window.render();
|
|
||||||
|
|
||||||
|
|
||||||
self.window.wl_surface().damage_buffer(0, 0, self.width as i32, self.height as i32);
|
|
||||||
self.window.wl_surface().frame(qh, self.window.wl_surface().clone());
|
|
||||||
buffer.attach_to(self.window.wl_surface()).expect("buffer attach");
|
|
||||||
self.window.commit();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate_compositor!(SimpleWindow);
|
|
||||||
delegate_output!(SimpleWindow);
|
|
||||||
delegate_shm!(SimpleWindow);
|
|
||||||
|
|
||||||
delegate_seat!(SimpleWindow);
|
|
||||||
|
|
||||||
delegate_xdg_shell!(SimpleWindow);
|
|
||||||
delegate_xdg_window!(SimpleWindow);
|
|
||||||
delegate_activation!(SimpleWindow);
|
|
||||||
|
|
||||||
delegate_pointer!(SimpleWindow);
|
|
||||||
|
|
||||||
delegate_registry!(SimpleWindow);
|
|
||||||
|
|
||||||
impl ProvidesRegistryState for SimpleWindow {
|
|
||||||
fn registry(&mut self) -> &mut RegistryState {
|
|
||||||
&mut self.registry_state
|
|
||||||
}
|
|
||||||
registry_handlers![OutputState, SeatState,];
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,541 @@
|
|||||||
|
use smithay_client_toolkit::reexports::calloop::{EventLoop, LoopHandle}; use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource;
|
||||||
|
use smithay_client_toolkit::{
|
||||||
|
activation::{ActivationState, ActivationHandler, RequestData},
|
||||||
|
compositor::{CompositorState, CompositorHandler},
|
||||||
|
output::{OutputHandler, OutputState},
|
||||||
|
globals::GlobalData,
|
||||||
|
registry::{self, RegistryState, ProvidesRegistryState, RegistryHandler, SimpleGlobal},
|
||||||
|
registry_handlers,
|
||||||
|
seat::{
|
||||||
|
pointer::{PointerEvent, PointerEventKind, PointerHandler},
|
||||||
|
SeatState, SeatHandler, Capability
|
||||||
|
},
|
||||||
|
shell::{
|
||||||
|
xdg::{
|
||||||
|
window::{WindowHandler, Window, WindowConfigure, WindowDecorations},
|
||||||
|
XdgShell,
|
||||||
|
},
|
||||||
|
WaylandSurface,
|
||||||
|
},
|
||||||
|
shm::{ slot::{SlotPool, Buffer},
|
||||||
|
Shm, ShmHandler
|
||||||
|
},
|
||||||
|
delegate_registry, delegate_compositor, delegate_seat, delegate_output,
|
||||||
|
delegate_xdg_shell, delegate_shm, delegate_activation, delegate_xdg_window,
|
||||||
|
delegate_pointer, delegate_simple,
|
||||||
|
};
|
||||||
|
|
||||||
|
use wayland_client::{
|
||||||
|
Connection, QueueHandle, Proxy, Dispatch,
|
||||||
|
globals::{registry_queue_init, GlobalList},
|
||||||
|
protocol::{wl_surface::{self, WlSurface}, wl_output, wl_seat, wl_shm, wl_pointer, wl_keyboard},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
use wayland_protocols_misc::zwp_input_method_v2::client::{
|
||||||
|
zwp_input_method_v2::{self, ZwpInputMethodV2},
|
||||||
|
zwp_input_popup_surface_v2::{self, ZwpInputPopupSurfaceV2},
|
||||||
|
zwp_input_method_manager_v2::{self, ZwpInputMethodManagerV2},
|
||||||
|
zwp_input_method_keyboard_grab_v2::{self, ZwpInputMethodKeyboardGrabV2},
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
use anyhow;
|
||||||
|
|
||||||
|
use crate::wayland::{
|
||||||
|
draw::DrawPath,
|
||||||
|
builder::{TextAreaBuilder, DrawAreaBuilder, ButtonBuilder},
|
||||||
|
ui::{self, Widget, ButtonType},
|
||||||
|
Position,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
use crate::process_point::print_words;
|
||||||
|
|
||||||
|
const WINDOW_HEIGHT: usize = 256;
|
||||||
|
const WINDOW_WIDTH: usize = 512;
|
||||||
|
|
||||||
|
|
||||||
|
pub(crate) struct SimpleWindow {
|
||||||
|
registry_state: RegistryState,
|
||||||
|
seat_state: SeatState,
|
||||||
|
output_state: OutputState,
|
||||||
|
compositor_state: CompositorState,
|
||||||
|
xdg_activation: Option<ActivationState>,
|
||||||
|
input_manager: SimpleGlobal<ZwpInputMethodManagerV2, 1>,
|
||||||
|
input_method: Option<ZwpInputMethodV2>,
|
||||||
|
keyboard_grab: Option<ZwpInputMethodKeyboardGrabV2>,
|
||||||
|
popup_surface: Option<ZwpInputPopupSurfaceV2>,
|
||||||
|
shm: Shm,
|
||||||
|
|
||||||
|
buffer: Option<Buffer>,
|
||||||
|
pool: SlotPool,
|
||||||
|
surface: Option<WlSurface>,
|
||||||
|
ui: Option<ui::Window>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
cursor_down: bool,
|
||||||
|
exit: bool,
|
||||||
|
first_configure: bool,
|
||||||
|
pointer: Option<wl_pointer::WlPointer>,
|
||||||
|
_loop_handle: LoopHandle<'static, SimpleWindow>,
|
||||||
|
|
||||||
|
draw_path: DrawPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleWindow {
|
||||||
|
pub(crate) fn run() -> anyhow::Result<()> {
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(3));
|
||||||
|
|
||||||
|
let conn = Connection::connect_to_env()?;
|
||||||
|
|
||||||
|
let (globals, event_queue) = registry_queue_init(&conn)?;
|
||||||
|
let qh = event_queue.handle();
|
||||||
|
let mut event_loop: EventLoop::<SimpleWindow> =
|
||||||
|
EventLoop::try_new()?;
|
||||||
|
let loop_handle = event_loop.handle();
|
||||||
|
WaylandSource::new(conn.clone(), event_queue).insert(loop_handle)?;
|
||||||
|
|
||||||
|
let input_manager = SimpleGlobal::<ZwpInputMethodManagerV2, 1>::bind(&globals, &qh)
|
||||||
|
.expect("zwp_input_method_manager_v2 not available");
|
||||||
|
let compositor = CompositorState::bind(&globals, &qh)?;
|
||||||
|
let shm = Shm::bind(&globals, &qh)?;
|
||||||
|
|
||||||
|
let pool = SlotPool::new((WINDOW_WIDTH as usize) * (WINDOW_HEIGHT as usize) * 4, &shm)?;
|
||||||
|
|
||||||
|
let xdg_activation = ActivationState::bind(&globals, &qh).ok();
|
||||||
|
|
||||||
|
let mut simple_window = SimpleWindow {
|
||||||
|
registry_state: RegistryState::new(&globals),
|
||||||
|
seat_state: SeatState::new(&globals, &qh),
|
||||||
|
output_state: OutputState::new(&globals, &qh),
|
||||||
|
compositor_state: compositor,
|
||||||
|
xdg_activation,
|
||||||
|
input_manager,
|
||||||
|
input_method: None,
|
||||||
|
keyboard_grab: None,
|
||||||
|
popup_surface: None,
|
||||||
|
|
||||||
|
shm,
|
||||||
|
buffer: None,
|
||||||
|
pool,
|
||||||
|
surface: None,
|
||||||
|
ui: None,
|
||||||
|
width: WINDOW_WIDTH,
|
||||||
|
height: WINDOW_HEIGHT,
|
||||||
|
cursor_down: false,
|
||||||
|
exit: false,
|
||||||
|
first_configure: true,
|
||||||
|
pointer: None,
|
||||||
|
_loop_handle: event_loop.handle(),
|
||||||
|
|
||||||
|
draw_path: DrawPath::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
event_loop.dispatch(Duration::from_millis(16), &mut simple_window)?;
|
||||||
|
|
||||||
|
if simple_window.first_configure {
|
||||||
|
if let Some(_) = simple_window.surface {
|
||||||
|
simple_window.configure(&conn, &qh);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
simple_window.draw(&conn, &qh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if simple_window.exit {
|
||||||
|
println!("exiting");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActivationHandler for SimpleWindow {
|
||||||
|
type RequestData = RequestData;
|
||||||
|
|
||||||
|
fn new_token(&mut self, token: String, data: &Self::RequestData) {
|
||||||
|
self.xdg_activation
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.activate::<SimpleWindow>(self.surface.as_ref().unwrap(), token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompositorHandler for SimpleWindow {
|
||||||
|
fn scale_factor_changed(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
_: &wl_surface::WlSurface,
|
||||||
|
_: i32) {}
|
||||||
|
fn transform_changed(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
_: &wl_surface::WlSurface,
|
||||||
|
_: wl_output::Transform) {}
|
||||||
|
fn frame(
|
||||||
|
&mut self,
|
||||||
|
conn: &Connection,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
_: &wl_surface::WlSurface,
|
||||||
|
_: u32
|
||||||
|
) {
|
||||||
|
self.draw(conn, qh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeatHandler for SimpleWindow {
|
||||||
|
fn seat_state(&mut self) -> &mut SeatState {
|
||||||
|
&mut self.seat_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_seat(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
_: wl_seat::WlSeat
|
||||||
|
) {}
|
||||||
|
fn new_capability(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
seat: wl_seat::WlSeat,
|
||||||
|
capability: Capability,
|
||||||
|
) {
|
||||||
|
if capability == Capability::Pointer && self.pointer.is_none() && self.input_method.is_some() {
|
||||||
|
println!("Set pointer capability");
|
||||||
|
let pointer = self.seat_state.get_pointer(qh, &seat).expect("Failed to create pointer");
|
||||||
|
self.pointer = Some(pointer);
|
||||||
|
} else if capability == Capability::Keyboard && self.input_method.is_none() {
|
||||||
|
let input_method = self.input_manager
|
||||||
|
.get()
|
||||||
|
.expect("failed to create input method")
|
||||||
|
.get_input_method(&seat, qh, ());
|
||||||
|
|
||||||
|
let surface = self.compositor_state.create_surface(&qh);
|
||||||
|
self.popup_surface = Some(input_method.get_input_popup_surface(&surface, &qh, ()));
|
||||||
|
// self.keyboard_grab = Some(input_method.grab_keyboard(&qh, ()));
|
||||||
|
self.surface = Some(surface);
|
||||||
|
self.input_method = Some(input_method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn remove_capability(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
_: wl_seat::WlSeat,
|
||||||
|
capability: Capability,
|
||||||
|
) {
|
||||||
|
if capability == Capability::Pointer && self.pointer.is_some() {
|
||||||
|
println!("Unset pointer capability");
|
||||||
|
self.pointer.take().unwrap().release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn remove_seat(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
_: wl_seat::WlSeat,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputHandler for SimpleWindow {
|
||||||
|
fn output_state(&mut self) -> &mut OutputState {
|
||||||
|
&mut self.output_state
|
||||||
|
}
|
||||||
|
fn new_output(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
_: wl_output::WlOutput,
|
||||||
|
) {}
|
||||||
|
fn update_output(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
_: wl_output::WlOutput,
|
||||||
|
) {}
|
||||||
|
fn output_destroyed(
|
||||||
|
&mut self,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
_: wl_output::WlOutput,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ShmHandler for SimpleWindow {
|
||||||
|
fn shm_state(&mut self) -> &mut Shm {
|
||||||
|
&mut self.shm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerHandler for SimpleWindow {
|
||||||
|
fn pointer_frame(
|
||||||
|
&mut self,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qh: &QueueHandle<Self>,
|
||||||
|
_pointer: &wl_pointer::WlPointer,
|
||||||
|
events: &[PointerEvent],
|
||||||
|
) {
|
||||||
|
use PointerEventKind::*;
|
||||||
|
for event in events {
|
||||||
|
// Ignore events for other surfaces
|
||||||
|
|
||||||
|
match event.kind {
|
||||||
|
Enter { .. } => {
|
||||||
|
println!("Pointer entered @{:?}", event.position);
|
||||||
|
}
|
||||||
|
Leave { .. } => {
|
||||||
|
println!("Pointer left");
|
||||||
|
}
|
||||||
|
Motion { .. } => {
|
||||||
|
if self.cursor_down {
|
||||||
|
if let Some(window) = &mut self.ui {
|
||||||
|
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
|
||||||
|
if window.draw_area.contains_point(pos) {
|
||||||
|
window.draw_area.path.add_point(pos, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Press { button, .. } => {
|
||||||
|
println!("Press {:x} @ {:?}", button, event.position);
|
||||||
|
self.cursor_down = true;
|
||||||
|
if let Some(window) = &mut self.ui {
|
||||||
|
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
|
||||||
|
if window.draw_area.contains_point(pos) {
|
||||||
|
window.draw_area.path.add_point(pos, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for button in window.widgets
|
||||||
|
.iter()
|
||||||
|
.filter_map(|widget| {
|
||||||
|
if let Widget::Button(button) = widget {
|
||||||
|
Some(button)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
match button.button_type {
|
||||||
|
ButtonType::Clear if button.contains_point(pos) => {
|
||||||
|
window.draw_area.path = DrawPath::default();
|
||||||
|
},
|
||||||
|
ButtonType::Enter if button.contains_point(pos) => {
|
||||||
|
if let Some(input_method) = &self.input_method {
|
||||||
|
input_method.commit_string(String::from("aaa"));
|
||||||
|
};
|
||||||
|
print_words((&window.draw_area).into()).unwrap();
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Release { button, .. } => {
|
||||||
|
println!("Release {:x} @ {:?}", button, event.position);
|
||||||
|
self.cursor_down = false;
|
||||||
|
if let Some(window) = &mut self.ui {
|
||||||
|
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
|
||||||
|
if window.draw_area.contains_point(pos) {
|
||||||
|
window.draw_area.path.add_point(pos, false);
|
||||||
|
} else {
|
||||||
|
println!("Does not contain point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Axis { horizontal, vertical, .. } => {
|
||||||
|
println!("Scroll H:{horizontal:?}, V:{vertical:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleWindow {
|
||||||
|
fn draw(&mut self, _: &Connection, qh: &QueueHandle<Self>) {
|
||||||
|
let buffer = self.buffer.get_or_insert_with(|| {
|
||||||
|
self.pool
|
||||||
|
.create_buffer(self.width as i32, self.height as i32, (self.width as i32) * 4, wl_shm::Format::Argb8888)
|
||||||
|
.expect("create buffer")
|
||||||
|
.0
|
||||||
|
});
|
||||||
|
|
||||||
|
let canvas = match self.pool.canvas(buffer) {
|
||||||
|
Some(canvas) => canvas,
|
||||||
|
None => {
|
||||||
|
let (second_buffer, canvas) = self
|
||||||
|
.pool
|
||||||
|
.create_buffer(
|
||||||
|
self.width as i32,
|
||||||
|
self.height as i32,
|
||||||
|
(self.width as i32) * 4,
|
||||||
|
wl_shm::Format::Argb8888,
|
||||||
|
)
|
||||||
|
.expect("create buffer");
|
||||||
|
*buffer = second_buffer;
|
||||||
|
canvas
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if let Some(surface) = &self.surface {
|
||||||
|
if let Some(ui) = &mut self.ui {
|
||||||
|
|
||||||
|
ui.render(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
surface.damage_buffer(0, 0, self.width as i32, self.height as i32);
|
||||||
|
surface.frame(qh, surface.clone());
|
||||||
|
buffer.attach_to(surface).expect("buffer attach");
|
||||||
|
surface.commit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure(
|
||||||
|
&mut self,
|
||||||
|
conn: &Connection,
|
||||||
|
qh: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
if self.first_configure {
|
||||||
|
self.first_configure = false;
|
||||||
|
self.draw(conn, qh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ui.is_none() {
|
||||||
|
let mut draw_area = DrawAreaBuilder::new();
|
||||||
|
|
||||||
|
draw_area.position(0.0, 50.0);
|
||||||
|
draw_area.width(self.width - 100);
|
||||||
|
draw_area.height(self.height - 50);
|
||||||
|
draw_area.path(&mut self.draw_path);
|
||||||
|
|
||||||
|
let draw_area = draw_area.finish();
|
||||||
|
|
||||||
|
|
||||||
|
let mut ui = ui::Window::new(
|
||||||
|
self.width.try_into().unwrap(),
|
||||||
|
self.height.try_into().unwrap(),
|
||||||
|
draw_area,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut enter_button = ButtonBuilder::new();
|
||||||
|
|
||||||
|
enter_button.button_type(ButtonType::Enter);
|
||||||
|
enter_button.position(self.width - 100, 60);
|
||||||
|
enter_button.width(100);
|
||||||
|
enter_button.height(50);
|
||||||
|
let enter_button = enter_button.finish();
|
||||||
|
|
||||||
|
let mut clear_button = ButtonBuilder::new();
|
||||||
|
|
||||||
|
clear_button.button_type(ButtonType::Clear);
|
||||||
|
clear_button.position(self.width - 100, 120);
|
||||||
|
clear_button.width(100);
|
||||||
|
clear_button.height(50);
|
||||||
|
let clear_button = clear_button.finish();
|
||||||
|
|
||||||
|
let mut text_area = TextAreaBuilder::new();
|
||||||
|
text_area.position(0.0, 0.0);
|
||||||
|
text_area.width(self.width);
|
||||||
|
text_area.height(50);
|
||||||
|
let text_area = text_area.finish();
|
||||||
|
|
||||||
|
|
||||||
|
ui.add_widget(enter_button);
|
||||||
|
ui.add_widget(clear_button);
|
||||||
|
ui.add_widget(text_area);
|
||||||
|
|
||||||
|
self.ui = Some(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate_compositor!(SimpleWindow);
|
||||||
|
delegate_output!(SimpleWindow);
|
||||||
|
delegate_shm!(SimpleWindow);
|
||||||
|
delegate_activation!(SimpleWindow);
|
||||||
|
|
||||||
|
delegate_seat!(SimpleWindow);
|
||||||
|
|
||||||
|
delegate_pointer!(SimpleWindow);
|
||||||
|
|
||||||
|
delegate_registry!(SimpleWindow);
|
||||||
|
|
||||||
|
delegate_simple!(SimpleWindow, ZwpInputMethodManagerV2, 1);
|
||||||
|
|
||||||
|
impl ProvidesRegistryState for SimpleWindow {
|
||||||
|
fn registry(&mut self) -> &mut RegistryState {
|
||||||
|
&mut self.registry_state
|
||||||
|
}
|
||||||
|
registry_handlers![OutputState, SeatState];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ZwpInputMethodV2, ()> for SimpleWindow {
|
||||||
|
fn event(
|
||||||
|
_: &mut Self,
|
||||||
|
_: &ZwpInputMethodV2,
|
||||||
|
event: zwp_input_method_v2::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
zwp_input_method_v2::Event::Done => println!("input method done"),
|
||||||
|
zwp_input_method_v2::Event::Activate => println!("input method activated"),
|
||||||
|
zwp_input_method_v2::Event::Deactivate => println!("input method deactivated"),
|
||||||
|
zwp_input_method_v2::Event::ContentType { hint, purpose } => println!("content type, hint: {:#?}, purpose: {:#?}", hint, purpose),
|
||||||
|
zwp_input_method_v2::Event::Unavailable => println!("input method unavailable"),
|
||||||
|
zwp_input_method_v2::Event::SurroundingText { text, cursor, anchor } => println!("surrounding text, text: {:#?}, cursor: {:#?}, anchor: {:#?}", text, cursor, anchor),
|
||||||
|
zwp_input_method_v2::Event::TextChangeCause { cause } => println!("input change cause: {:#?}", cause),
|
||||||
|
_ => unreachable!("zwp_input_method_v2 has no other events"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ZwpInputPopupSurfaceV2, ()> for SimpleWindow {
|
||||||
|
fn event(
|
||||||
|
_: &mut Self,
|
||||||
|
_: &ZwpInputPopupSurfaceV2,
|
||||||
|
event: zwp_input_popup_surface_v2::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
zwp_input_popup_surface_v2::Event::TextInputRectangle { x, y, width, height } => println!("New text input rectangle at {}, {}", x, y),
|
||||||
|
_ => unreachable!("zwp_input_popup_surface_v2::Event only contains TextInputRectangle"),
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ZwpInputMethodKeyboardGrabV2, ()> for SimpleWindow {
|
||||||
|
fn event(
|
||||||
|
_: &mut Self,
|
||||||
|
_: &ZwpInputMethodKeyboardGrabV2,
|
||||||
|
event: zwp_input_method_keyboard_grab_v2::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
zwp_input_method_keyboard_grab_v2::Event::Key { serial, time, key, state } => {},
|
||||||
|
zwp_input_method_keyboard_grab_v2::Event::Keymap { format, fd, size } => {},
|
||||||
|
zwp_input_method_keyboard_grab_v2::Event::Modifiers { serial, mods_depressed, mods_latched, mods_locked, group } => {},
|
||||||
|
zwp_input_method_keyboard_grab_v2::Event::RepeatInfo { rate, delay } => {},
|
||||||
|
_ => unreachable!("zwp_input_method_keyboard_grab_v2::Event only has Key, Keymap, Modifiers, and RepeatInfo"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
196
src/wayland/builder.rs
Normal file
196
src/wayland/builder.rs
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
use crate::wayland::{
|
||||||
|
ui::{TextArea, TextAreaColors, Button, ButtonColors, Widget, ButtonType},
|
||||||
|
draw::{DrawAreaColors, DrawArea, DrawPath},
|
||||||
|
Position
|
||||||
|
};
|
||||||
|
|
||||||
|
use raqote::{DrawOptions, PathBuilder};
|
||||||
|
|
||||||
|
pub(super) struct TextAreaBuilder {
|
||||||
|
pub(super) width: Option<usize>,
|
||||||
|
pub(super) height: Option<usize>,
|
||||||
|
pub(super) position: Option<Position>,
|
||||||
|
pub(super) colors: Option<TextAreaColors>,
|
||||||
|
pub(super) options: Option<DrawOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TextAreaBuilder {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
width: None,
|
||||||
|
height: None,
|
||||||
|
position: None,
|
||||||
|
colors: None,
|
||||||
|
options: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn width(&mut self, width: usize) {
|
||||||
|
self.width = Some(width)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn height(&mut self, height: usize) {
|
||||||
|
self.height = Some(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn position(&mut self, x: f32, y: f32) {
|
||||||
|
self.position = Some(Position::new(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(super) fn colors(&mut self, colors: TextAreaColors) {
|
||||||
|
self.colors = Some(colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn finish(self) -> Widget {
|
||||||
|
Widget::TextArea(
|
||||||
|
TextArea {
|
||||||
|
width: self.width.expect("Button must have width"),
|
||||||
|
height: self.height.expect("Button must have height"),
|
||||||
|
position: self.position.expect("Button must have a position"),
|
||||||
|
colors: self.colors.unwrap_or_default(),
|
||||||
|
options: self.options.unwrap_or(DrawOptions::new()),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct ButtonBuilder {
|
||||||
|
pub(super) width: Option<usize>,
|
||||||
|
pub(super) height: Option<usize>,
|
||||||
|
pub(super) position: Option<Position>,
|
||||||
|
pub(super) colors: Option<ButtonColors>,
|
||||||
|
pub(super) options: Option<DrawOptions>,
|
||||||
|
pub(super) button_type: Option<ButtonType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ButtonBuilder {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
width: None,
|
||||||
|
height: None,
|
||||||
|
position: None,
|
||||||
|
colors: None,
|
||||||
|
options: None,
|
||||||
|
button_type: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn width(&mut self, width: usize) {
|
||||||
|
self.width = Some(width)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn height(&mut self, height: usize) {
|
||||||
|
self.height = Some(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn position(&mut self, x: usize, y: usize) {
|
||||||
|
self.position = Some(Position::new(x as f32, y as f32))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(super) fn colors(&mut self, colors: ButtonColors) {
|
||||||
|
self.colors = Some(colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn button_type(&mut self, button_type: ButtonType) {
|
||||||
|
self.button_type = Some(button_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn finish(self) -> Widget {
|
||||||
|
let width = self.width.expect("Button must have a width");
|
||||||
|
let height = self.height.expect("Button must have a height");
|
||||||
|
let position = self.position.expect("Button must have a position");
|
||||||
|
|
||||||
|
let mut button_path = PathBuilder::new();
|
||||||
|
button_path.move_to(position.x, position.y);
|
||||||
|
button_path.line_to(position.x + width as f32, position.y);
|
||||||
|
button_path.line_to(position.x + width as f32, position.y + height as f32);
|
||||||
|
button_path.line_to(position.x, position.y + height as f32);
|
||||||
|
button_path.line_to(position.x, position.y);
|
||||||
|
let button_path = button_path.finish();
|
||||||
|
|
||||||
|
Widget::Button(
|
||||||
|
Button {
|
||||||
|
button_type: self.button_type.expect("Button must have a type"),
|
||||||
|
width: self.width.expect("Button must have width"),
|
||||||
|
height: self.height.expect("Button must have height"),
|
||||||
|
position: self.position.expect("Button must have a position"),
|
||||||
|
shape: button_path,
|
||||||
|
colors: self.colors.unwrap_or_default(),
|
||||||
|
options: self.options.unwrap_or(DrawOptions::new()),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct DrawAreaBuilder {
|
||||||
|
pub(super) width: Option<usize>,
|
||||||
|
pub(super) height: Option<usize>,
|
||||||
|
pub(super) position: Option<Position>,
|
||||||
|
pub(super) path: Option<DrawPath>,
|
||||||
|
pub(super) colors: Option<DrawAreaColors>,
|
||||||
|
pub(super) options: Option<DrawOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawAreaBuilder {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
width: None,
|
||||||
|
height: None,
|
||||||
|
position: None,
|
||||||
|
path: None,
|
||||||
|
colors: None,
|
||||||
|
options: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn width(&mut self, width: usize) {
|
||||||
|
self.width = Some(width)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn height(&mut self, height: usize) {
|
||||||
|
self.height = Some(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn position(&mut self, x: f32, y: f32) {
|
||||||
|
self.position = Some(Position::new(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(super) fn colors(&mut self, colors: DrawAreaColors) {
|
||||||
|
self.colors = Some(colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn path(&mut self, path: &mut DrawPath) {
|
||||||
|
self.path = Some(DrawPath {
|
||||||
|
paths: std::mem::take(&mut path.paths),
|
||||||
|
newline: path.newline,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn finish(self) -> DrawArea {
|
||||||
|
|
||||||
|
let width = self.width.expect("Button must have a width");
|
||||||
|
let height = self.height.expect("Button must have a height");
|
||||||
|
let position = self.position.expect("Button must have a position");
|
||||||
|
|
||||||
|
let mut draw_rect_path = PathBuilder::new();
|
||||||
|
draw_rect_path.move_to(position.x, position.y);
|
||||||
|
draw_rect_path.line_to(position.x + width as f32, position.y);
|
||||||
|
draw_rect_path.line_to(position.x + width as f32, position.y + height as f32);
|
||||||
|
draw_rect_path.line_to(position.x, position.y + height as f32);
|
||||||
|
draw_rect_path.line_to(position.x, position.y);
|
||||||
|
let draw_rect_path = draw_rect_path.finish();
|
||||||
|
|
||||||
|
DrawArea {
|
||||||
|
width: self.width.expect("Draw area must have width"),
|
||||||
|
height: self.height.expect("Draw area must have height"),
|
||||||
|
position: self.position.expect("Draw area must have a position"),
|
||||||
|
path: self.path.expect("Draw must have a path"),
|
||||||
|
shape: draw_rect_path,
|
||||||
|
colors: self.colors.unwrap_or_default(),
|
||||||
|
options: self.options.unwrap_or(DrawOptions::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
209
src/wayland/draw.rs
Normal file
209
src/wayland/draw.rs
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
use raqote::{DrawTarget, Color, StrokeStyle, PathBuilder, Source, DrawOptions, SolidSource};
|
||||||
|
use rten_tensor::NdTensor;
|
||||||
|
|
||||||
|
use crate::wayland::Position;
|
||||||
|
|
||||||
|
pub(super) trait Draw {
|
||||||
|
fn render(&self, area: &mut DrawTarget<&mut [u32]>);
|
||||||
|
}
|
||||||
|
|
||||||
|
type Path = Vec<Position>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct DrawPath {
|
||||||
|
pub(crate) paths: Vec<Path>,
|
||||||
|
pub(crate) newline: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawPath {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
paths: Vec::new(),
|
||||||
|
newline: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_point(&mut self, point: Position, newline: bool) {
|
||||||
|
if newline {
|
||||||
|
self.paths.push(vec![point])
|
||||||
|
} else if self.paths.len() > 0 {
|
||||||
|
self.paths
|
||||||
|
.last_mut()
|
||||||
|
.unwrap()
|
||||||
|
.push(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) fn into_iter(self) -> std::vec::IntoIter<Path> {
|
||||||
|
self.paths.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn iter(&self) -> std::slice::Iter<Path> {
|
||||||
|
self.paths.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) fn iter_mut(&mut self) -> std::slice::IterMut<Path> {
|
||||||
|
self.paths.iter_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(super) struct DrawAreaColors {
|
||||||
|
background_color: Color,
|
||||||
|
drawing_color: Color,
|
||||||
|
border_color: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DrawAreaColors {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
background_color: Color::new(0xff, 0x4a, 0x4e, 0x64),
|
||||||
|
drawing_color: Color::new(0xff, 0xea, 0xea, 0xe6),
|
||||||
|
border_color: Color::new(0xff, 0x77, 0x7f, 0x97),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct DrawArea {
|
||||||
|
pub(super) width: usize,
|
||||||
|
pub(super) height: usize,
|
||||||
|
pub(super) position: Position,
|
||||||
|
pub(super) path: DrawPath,
|
||||||
|
pub(super) shape: raqote::Path,
|
||||||
|
pub(super) colors: DrawAreaColors,
|
||||||
|
pub(super) options: DrawOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawArea {
|
||||||
|
fn background_source(&self) -> Source {
|
||||||
|
Source::Solid(self.colors.background_color.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drawing_source(&self) -> Source {
|
||||||
|
Source::Solid(self.colors.drawing_color.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn border_source(&self) -> Source {
|
||||||
|
Source::Solid(self.colors.border_color.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn contains_point(&self, point: Position) -> bool {
|
||||||
|
self.shape.contains_point(0.1, point.x, point.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&DrawArea> for NdTensor<f32, 3> {
|
||||||
|
fn from(draw_area: &DrawArea) -> Self {
|
||||||
|
let mut target = DrawTarget::new(draw_area.width as i32, draw_area.height as i32);
|
||||||
|
|
||||||
|
target.fill_rect(
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
draw_area.width as f32,
|
||||||
|
draw_area.height as f32,
|
||||||
|
&Source::Solid(SolidSource::from_unpremultiplied_argb(0xff, 0xff, 0xff, 0xff)),
|
||||||
|
&DrawOptions::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut draw_path = PathBuilder::new();
|
||||||
|
|
||||||
|
for path in draw_area.path.iter() {
|
||||||
|
if path.len() > 1 {
|
||||||
|
let mut path = path
|
||||||
|
.into_iter()
|
||||||
|
.map(|position| Position::new(position.x - draw_area.position.x, position.y - draw_area.position.y));
|
||||||
|
|
||||||
|
let first_point = path
|
||||||
|
.by_ref()
|
||||||
|
.next();
|
||||||
|
|
||||||
|
if let Some(point) = first_point {
|
||||||
|
draw_path.move_to(point.x, point.y);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for point in path {
|
||||||
|
draw_path.line_to(point.x, point.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let draw_path = draw_path.finish();
|
||||||
|
|
||||||
|
target.stroke(
|
||||||
|
&draw_path,
|
||||||
|
&Source::Solid(SolidSource::from_unpremultiplied_argb(0xff, 0x00, 0x00, 0x00)),
|
||||||
|
&StrokeStyle {width: 3.0, ..StrokeStyle::default()},
|
||||||
|
&DrawOptions::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let data: Vec<f32> = target
|
||||||
|
.get_data_u8()
|
||||||
|
.chunks(4)
|
||||||
|
.into_iter()
|
||||||
|
.map(|chunk| chunk[0..3].into_iter().map(|byte| *byte as f32).sum::<f32>() / 765.0)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
image::save_buffer_with_format(
|
||||||
|
"./image.png",
|
||||||
|
&data
|
||||||
|
.iter()
|
||||||
|
.map(|num| (num * 255.0).floor() as u8)
|
||||||
|
.collect::<Vec<u8>>(),
|
||||||
|
draw_area.width as u32,
|
||||||
|
draw_area.height as u32,
|
||||||
|
image::ColorType::L8,
|
||||||
|
image::ImageFormat::Png
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
NdTensor::from_data([1, draw_area.height, draw_area.width], data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Draw for DrawArea {
|
||||||
|
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
||||||
|
let mut draw_path = PathBuilder::new();
|
||||||
|
|
||||||
|
for path in self.path.iter() {
|
||||||
|
if path.len() > 1 {
|
||||||
|
let mut path = path.into_iter();
|
||||||
|
|
||||||
|
let first_point = path
|
||||||
|
.by_ref()
|
||||||
|
.next();
|
||||||
|
|
||||||
|
draw_path.move_to(first_point.unwrap().x, first_point.unwrap().y);
|
||||||
|
|
||||||
|
for point in path {
|
||||||
|
draw_path.line_to(point.x, point.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let path = draw_path.finish();
|
||||||
|
|
||||||
|
area.fill(
|
||||||
|
&self.shape,
|
||||||
|
&self.background_source(),
|
||||||
|
&self.options,
|
||||||
|
);
|
||||||
|
|
||||||
|
area.stroke(
|
||||||
|
&path,
|
||||||
|
&self.drawing_source(),
|
||||||
|
&StrokeStyle {width: 3.0, ..StrokeStyle::default()},
|
||||||
|
&self.options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
use raqote::{DrawTarget, PathBuilder, Source, StrokeStyle, DrawOptions, SolidSource, Color};
|
use raqote::{DrawTarget, Source, DrawOptions, Color};
|
||||||
|
|
||||||
struct UIColor {
|
use crate::wayland::{
|
||||||
|
draw::{Draw, DrawArea},
|
||||||
|
Position,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(super) struct UIColors {
|
||||||
draw_area: Color,
|
draw_area: Color,
|
||||||
button: Color,
|
button: Color,
|
||||||
button_accent: Color,
|
button_accent: Color,
|
||||||
@@ -10,7 +16,7 @@ struct UIColor {
|
|||||||
_text: Color,
|
_text: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UIColor {
|
impl Default for UIColors {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
draw_area: Color::new(0xff, 0x15, 0x15, 0x15),
|
draw_area: Color::new(0xff, 0x15, 0x15, 0x15),
|
||||||
@@ -24,35 +30,44 @@ impl Default for UIColor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct Window<'a, 'b> {
|
pub(super) enum Widget {
|
||||||
pub(super) draw: DrawTarget<&'a mut [u32]>,
|
Button(Button),
|
||||||
pub(super) options: DrawOptions,
|
TextArea(TextArea)
|
||||||
pub(super) colors: UIColor,
|
|
||||||
pub(super) widgets: Vec<Widget<'b>>,
|
|
||||||
pub(super) width: usize,
|
|
||||||
pub(super) height: usize,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Window<'a, 'b> {
|
pub(super) struct Window {
|
||||||
pub(super) fn new(buffer: &'a mut [u32], width: usize, height: usize) -> Self {
|
pub(super) width: usize,
|
||||||
|
pub(super) height: usize,
|
||||||
|
pub(super) widgets: Vec<Widget>,
|
||||||
|
pub(super) draw_area: DrawArea,
|
||||||
|
pub(super) colors: UIColors,
|
||||||
|
pub(super) options: DrawOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
pub(super) fn new(width: usize, height: usize, draw_area: DrawArea) -> Self {
|
||||||
Self {
|
Self {
|
||||||
draw: DrawTarget::from_backing(width.try_into().unwrap(), height.try_into().unwrap(), buffer),
|
|
||||||
options: DrawOptions::new(),
|
|
||||||
colors: UIColor::default(),
|
|
||||||
widgets: Vec::new(),
|
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
widgets: Vec::new(),
|
||||||
|
draw_area,
|
||||||
|
colors: UIColors::default(),
|
||||||
|
options: DrawOptions::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn add_widget(&mut self, widget: Widget<'b>) {
|
pub(super) fn add_widget(&mut self, widget: Widget) {
|
||||||
self.widgets.push(widget);
|
self.widgets.push(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn render(&mut self) {
|
pub(super) fn render(&self, canvas: &mut [u8]) {
|
||||||
|
|
||||||
self.draw.fill_rect(
|
// TODO: Make sure this is actually safe
|
||||||
|
let draw_buffer: &mut [u32] = unsafe {canvas.align_to_mut::<u32>().1};
|
||||||
|
|
||||||
|
let mut target = DrawTarget::from_backing(self.width as i32, self.height as i32, draw_buffer);
|
||||||
|
|
||||||
|
target.fill_rect(
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
self.width as f32,
|
self.width as f32,
|
||||||
@@ -61,103 +76,19 @@ impl<'a, 'b> Window<'a, 'b> {
|
|||||||
&self.options
|
&self.options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.draw_area.render(&mut target);
|
||||||
|
|
||||||
for widget in self.widgets.iter() {
|
for widget in self.widgets.iter() {
|
||||||
match widget {
|
match widget {
|
||||||
Widget::Button(button) => button.render(&mut self.draw),
|
Widget::Button(button) => button.render(&mut target),
|
||||||
Widget::DrawArea(draw_area) => draw_area.render(&mut self.draw),
|
Widget::TextArea(text_area) => text_area.render(&mut target),
|
||||||
Widget::TextArea(text_area) => text_area.render(&mut self.draw),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub(super) struct Position {
|
|
||||||
pub(super) x: f32,
|
|
||||||
pub(super) y: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct Path(Vec<Position>);
|
|
||||||
|
|
||||||
impl IntoIterator for Path {
|
|
||||||
type Item = Position;
|
|
||||||
type IntoIter = std::vec::IntoIter<Position>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a Path {
|
|
||||||
type Item = &'a Position;
|
|
||||||
type IntoIter = std::slice::Iter<'a, Position>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct DrawPath {
|
|
||||||
pub(super) paths: Vec<Path>,
|
|
||||||
pub(super) newline: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DrawPath {
|
|
||||||
pub(super) fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
paths: Vec::new(),
|
|
||||||
newline: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn add_point(&mut self, point: Position, newline: bool) {
|
|
||||||
if newline {
|
|
||||||
self.paths.push(Path(vec![point]))
|
|
||||||
} else if self.paths.len() > 0 {
|
|
||||||
self.paths
|
|
||||||
.last_mut()
|
|
||||||
.unwrap()
|
|
||||||
.0
|
|
||||||
.push(point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for DrawPath {
|
|
||||||
type Item = Path;
|
|
||||||
type IntoIter = std::vec::IntoIter<Path>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.paths.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a DrawPath {
|
|
||||||
type Item = &'a Path;
|
|
||||||
type IntoIter = std::slice::Iter<'a, Path>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.paths.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Position {
|
|
||||||
pub(super) fn new(x: f32, y: f32) -> Self {
|
|
||||||
Position { x, y }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) enum Widget<'a> {
|
|
||||||
Button(Button),
|
|
||||||
DrawArea(DrawArea<'a>),
|
|
||||||
TextArea(TextArea)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) trait Draw {
|
|
||||||
fn render(&self, area: &mut DrawTarget<&mut [u32]>);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub(super) struct ButtonColors {
|
pub(super) struct ButtonColors {
|
||||||
background: Color,
|
background: Color,
|
||||||
border: Color,
|
border: Color,
|
||||||
@@ -174,10 +105,19 @@ impl Default for ButtonColors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub(super) enum ButtonType {
|
||||||
|
Clear,
|
||||||
|
Enter,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub(super) struct Button {
|
pub(super) struct Button {
|
||||||
pub(super) width: f32,
|
pub(super) button_type: ButtonType,
|
||||||
pub(super) height: f32,
|
pub(super) width: usize,
|
||||||
|
pub(super) height: usize,
|
||||||
pub(super) position: Position,
|
pub(super) position: Position,
|
||||||
|
pub(super) shape: raqote::Path,
|
||||||
pub(super) colors: ButtonColors,
|
pub(super) colors: ButtonColors,
|
||||||
pub(super) options: DrawOptions,
|
pub(super) options: DrawOptions,
|
||||||
}
|
}
|
||||||
@@ -186,208 +126,25 @@ impl Button {
|
|||||||
fn background_source(&self) -> Source {
|
fn background_source(&self) -> Source {
|
||||||
Source::Solid(self.colors.background.into())
|
Source::Solid(self.colors.background.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn contains_point(&self, point: Position) -> bool {
|
||||||
|
self.shape.contains_point(0.1, point.x, point.y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct ButtonBuilder {
|
|
||||||
pub(super) width: Option<f32>,
|
|
||||||
pub(super) height: Option<f32>,
|
|
||||||
pub(super) position: Option<Position>,
|
|
||||||
pub(super) colors: Option<ButtonColors>,
|
|
||||||
pub(super) options: Option<DrawOptions>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ButtonBuilder {
|
|
||||||
pub(super) fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
width: None,
|
|
||||||
height: None,
|
|
||||||
position: None,
|
|
||||||
colors: None,
|
|
||||||
options: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn width(&mut self, width: f32) {
|
|
||||||
self.width = Some(width)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn height(&mut self, height: f32) {
|
|
||||||
self.height = Some(height)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn position(&mut self, x: f32, y: f32) {
|
|
||||||
self.position = Some(Position::new(x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn colors(&mut self, colors: ButtonColors) {
|
|
||||||
self.colors = Some(colors)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn finish(self) -> Widget<'a> {
|
|
||||||
Widget::Button(
|
|
||||||
Button {
|
|
||||||
width: self.width.expect("Button must have width"),
|
|
||||||
height: self.height.expect("Button must have height"),
|
|
||||||
position: self.position.expect("Button must have a position"),
|
|
||||||
colors: self.colors.unwrap_or_default(),
|
|
||||||
options: self.options.unwrap_or(DrawOptions::new()),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Draw for Button {
|
impl Draw for Button {
|
||||||
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
||||||
|
|
||||||
(*area).fill_rect(
|
area.fill(
|
||||||
self.position.x,
|
&self.shape,
|
||||||
self.position.y,
|
|
||||||
self.width,
|
|
||||||
self.height,
|
|
||||||
&self.background_source(),
|
|
||||||
&self.options,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct DrawAreaColors {
|
|
||||||
background_color: Color,
|
|
||||||
drawing_color: Color,
|
|
||||||
border_color: Color,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DrawAreaColors {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
background_color: Color::new(0xff, 0x4a, 0x4e, 0x64),
|
|
||||||
drawing_color: Color::new(0xff, 0xea, 0xea, 0xe6),
|
|
||||||
border_color: Color::new(0xff, 0x77, 0x7f, 0x97),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct DrawArea<'a> {
|
|
||||||
pub(super) width: f32,
|
|
||||||
pub(super) height: f32,
|
|
||||||
pub(super) position: Position,
|
|
||||||
pub(super) path: &'a DrawPath,
|
|
||||||
pub(super) colors: DrawAreaColors,
|
|
||||||
pub(super) options: DrawOptions,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DrawArea<'_> {
|
|
||||||
fn background_source(&self) -> Source {
|
|
||||||
Source::Solid(self.colors.background_color.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drawing_source(&self) -> Source {
|
|
||||||
Source::Solid(self.colors.drawing_color.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn border_source(&self) -> Source {
|
|
||||||
Source::Solid(self.colors.border_color.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub(super) struct DrawAreaBuilder<'a> {
|
|
||||||
pub(super) width: Option<f32>,
|
|
||||||
pub(super) height: Option<f32>,
|
|
||||||
pub(super) position: Option<Position>,
|
|
||||||
pub(super) path: Option<&'a DrawPath>,
|
|
||||||
pub(super) colors: Option<DrawAreaColors>,
|
|
||||||
pub(super) options: Option<DrawOptions>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DrawAreaBuilder<'a> {
|
|
||||||
pub(super) fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
width: None,
|
|
||||||
height: None,
|
|
||||||
position: None,
|
|
||||||
path: None,
|
|
||||||
colors: None,
|
|
||||||
options: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn width(&mut self, width: f32) {
|
|
||||||
self.width = Some(width)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn height(&mut self, height: f32) {
|
|
||||||
self.height = Some(height)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn position(&mut self, x: f32, y: f32) {
|
|
||||||
self.position = Some(Position::new(x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn colors(&mut self, colors: DrawAreaColors) {
|
|
||||||
self.colors = Some(colors)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn path(&mut self, path: &'a DrawPath) {
|
|
||||||
self.path = Some(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn finish(self) -> Widget<'a> {
|
|
||||||
Widget::DrawArea(
|
|
||||||
DrawArea {
|
|
||||||
width: self.width.expect("Draw area must have width"),
|
|
||||||
height: self.height.expect("Draw area must have height"),
|
|
||||||
position: self.position.expect("Draw area must have a position"),
|
|
||||||
path: self.path.expect("Draw must have a path"),
|
|
||||||
colors: self.colors.unwrap_or_default(),
|
|
||||||
options: self.options.unwrap_or(DrawOptions::new()),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Draw for DrawArea<'_> {
|
|
||||||
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
|
||||||
let mut draw_path = PathBuilder::new();
|
|
||||||
|
|
||||||
for path in self.path.into_iter() {
|
|
||||||
if path.0.len() > 1 {
|
|
||||||
let mut path = path.into_iter();
|
|
||||||
|
|
||||||
let first_point = path
|
|
||||||
.by_ref()
|
|
||||||
.next();
|
|
||||||
|
|
||||||
draw_path.move_to(first_point.unwrap().x, first_point.unwrap().y);
|
|
||||||
|
|
||||||
for point in path {
|
|
||||||
draw_path.line_to(point.x, point.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let path = draw_path.finish();
|
|
||||||
area.fill_rect(
|
|
||||||
self.position.x,
|
|
||||||
self.position.y,
|
|
||||||
self.width,
|
|
||||||
self.height,
|
|
||||||
&self.background_source(),
|
&self.background_source(),
|
||||||
&self.options,
|
&self.options,
|
||||||
);
|
);
|
||||||
|
|
||||||
area.stroke(
|
|
||||||
&path,
|
|
||||||
&self.drawing_source(),
|
|
||||||
&StrokeStyle {width: 3.0, ..StrokeStyle::default()},
|
|
||||||
&self.options,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub(super) struct TextAreaColors {
|
pub(super) struct TextAreaColors {
|
||||||
pub(super) background: Color,
|
pub(super) background: Color,
|
||||||
pub(super) text: Color,
|
pub(super) text: Color,
|
||||||
@@ -403,14 +160,15 @@ impl Default for TextAreaColors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct TextArea {
|
pub(super) struct TextArea {
|
||||||
pub(super) width: f32,
|
pub(super) width: usize,
|
||||||
pub(super) height: f32,
|
pub(super) height: usize,
|
||||||
pub(super) position: Position,
|
pub(super) position: Position,
|
||||||
pub(super) colors: TextAreaColors,
|
pub(super) colors: TextAreaColors,
|
||||||
pub(super) options: DrawOptions,
|
pub(super) options: DrawOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextArea {
|
impl TextArea {
|
||||||
|
#[allow(unused)]
|
||||||
pub(super) fn text_source(&self) -> Source {
|
pub(super) fn text_source(&self) -> Source {
|
||||||
Source::Solid(self.colors.text.into())
|
Source::Solid(self.colors.text.into())
|
||||||
}
|
}
|
||||||
@@ -420,69 +178,17 @@ impl TextArea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct TextAreaBuilder {
|
|
||||||
pub(super) width: Option<f32>,
|
|
||||||
pub(super) height: Option<f32>,
|
|
||||||
pub(super) position: Option<Position>,
|
|
||||||
pub(super) colors: Option<TextAreaColors>,
|
|
||||||
pub(super) options: Option<DrawOptions>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TextAreaBuilder {
|
|
||||||
pub(super) fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
width: None,
|
|
||||||
height: None,
|
|
||||||
position: None,
|
|
||||||
colors: None,
|
|
||||||
options: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn width(&mut self, width: f32) {
|
|
||||||
self.width = Some(width)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn height(&mut self, height: f32) {
|
|
||||||
self.height = Some(height)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn position(&mut self, x: f32, y: f32) {
|
|
||||||
self.position = Some(Position::new(x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub(super) fn colors(&mut self, colors: TextAreaColors) {
|
|
||||||
self.colors = Some(colors);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn finish(self) -> Widget<'a> {
|
|
||||||
Widget::TextArea(
|
|
||||||
TextArea {
|
|
||||||
width: self.width.expect("Button must have width"),
|
|
||||||
height: self.height.expect("Button must have height"),
|
|
||||||
position: self.position.expect("Button must have a position"),
|
|
||||||
colors: self.colors.unwrap_or_default(),
|
|
||||||
options: self.options.unwrap_or(DrawOptions::new()),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Draw for TextArea {
|
impl Draw for TextArea {
|
||||||
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
area.fill_rect(
|
area.fill_rect(
|
||||||
self.position.x,
|
self.position.x,
|
||||||
self.position.y,
|
self.position.y,
|
||||||
self.width,
|
self.width as f32,
|
||||||
self.height,
|
self.height as f32,
|
||||||
&self.background_source(),
|
&self.background_source(),
|
||||||
&self.options,
|
&self.options,
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user