updating and fixing some small performance issues
Some checks are pending
/ test (push) Waiting to run
Some checks are pending
/ test (push) Waiting to run
This commit is contained in:
parent
fd94f1dfa6
commit
a06890d9e1
10 changed files with 911 additions and 712 deletions
|
@ -6,6 +6,7 @@ pub mod presenter;
|
|||
pub mod song_editor;
|
||||
pub mod text_svg;
|
||||
pub mod video;
|
||||
pub mod widgets;
|
||||
|
||||
pub enum EditorMode {
|
||||
Song,
|
||||
|
|
|
@ -33,6 +33,7 @@ use url::Url;
|
|||
use crate::{
|
||||
core::{service_items::ServiceItemModel, slide::Slide},
|
||||
ui::text_svg::{self, Font as SvgFont},
|
||||
// ui::widgets::slide_text,
|
||||
BackgroundKind,
|
||||
};
|
||||
|
||||
|
@ -88,7 +89,7 @@ impl Presenter {
|
|||
gst::init().into_diagnostic()?;
|
||||
|
||||
let pipeline = format!(
|
||||
r#"playbin uri="{}" video-sink="videoscale ! videoconvert ! appsink name=iced_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1""#,
|
||||
r#"playbin uri="{}" video-sink="videoscale ! videoconvert ! appsink name=lumina_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1""#,
|
||||
url.as_str()
|
||||
);
|
||||
|
||||
|
@ -108,13 +109,14 @@ impl Presenter {
|
|||
.unwrap()
|
||||
.downcast::<gst::Bin>()
|
||||
.unwrap();
|
||||
let video_sink = bin.by_name("iced_video").unwrap();
|
||||
let video_sink = bin.by_name("lumina_video").unwrap();
|
||||
let video_sink =
|
||||
video_sink.downcast::<gst_app::AppSink>().unwrap();
|
||||
let result =
|
||||
Video::from_gst_pipeline(pipeline, video_sink, None);
|
||||
result.into_diagnostic()
|
||||
}
|
||||
|
||||
pub fn with_items(items: ServiceItemModel) -> Self {
|
||||
let slides = items.to_slides().unwrap_or_default();
|
||||
let video = {
|
||||
|
@ -545,75 +547,89 @@ pub(crate) fn slide_view(
|
|||
) -> Element<'_, Message> {
|
||||
responsive(move |size| {
|
||||
let width = size.height * 16.0 / 9.0;
|
||||
let font_size = scale_font(slide.font_size() as f32, width);
|
||||
let slide_text = slide.text();
|
||||
|
||||
// SVG based
|
||||
// let font = SvgFont::from(font).size(font_size.floor() as u8);
|
||||
// let text = text_svg::TextSvg::new()
|
||||
// .text(&slide_text)
|
||||
// .fill("#fff")
|
||||
// .shadow(text_svg::shadow(2, 2, 5, "#000000"))
|
||||
// .stroke(text_svg::stroke(3, "#000"))
|
||||
// .font(font)
|
||||
// .view()
|
||||
// .map(|m| Message::None);
|
||||
|
||||
// text widget based
|
||||
let lines = slide_text.lines();
|
||||
let text: Vec<Element<Message>> = lines
|
||||
.map(|t| {
|
||||
rich_text([span(format!("{}\n", t))
|
||||
.background(
|
||||
Background::Color(Color::BLACK)
|
||||
.scale_alpha(0.4),
|
||||
)
|
||||
.border(border::rounded(10))
|
||||
.padding(10)])
|
||||
.size(font_size)
|
||||
.font(font)
|
||||
.center()
|
||||
.into()
|
||||
// let chars: Vec<Span> = t
|
||||
// .chars()
|
||||
// .map(|c| -> Span {
|
||||
// let character: String = format!("{}/n", c);
|
||||
// span(character)
|
||||
// .size(font_size)
|
||||
// .font(font)
|
||||
// .background(
|
||||
// Background::Color(Color::BLACK)
|
||||
// .scale_alpha(0.4),
|
||||
// )
|
||||
// .border(border::rounded(10))
|
||||
// .padding(10)
|
||||
})
|
||||
.collect();
|
||||
let text = Column::with_children(text).spacing(26);
|
||||
|
||||
// let lines = slide_text.lines();
|
||||
// let stroke_text: Vec<Element<Message>> = lines
|
||||
// .map(|t| {
|
||||
// let mut stroke_font = font.clone();
|
||||
// stroke_font.stretch = Stretch::Condensed;
|
||||
// stroke_font.weight = Weight::Bold;
|
||||
// rich_text([span(format!("{}\n", t))
|
||||
// .size(font_size + 0.3)
|
||||
// .font(stroke_font)
|
||||
// .color(Color::BLACK)
|
||||
// .border(border::rounded(10))
|
||||
// .padding(10)])
|
||||
// .center()
|
||||
// .into()
|
||||
// })
|
||||
// .collect();
|
||||
// let stroke_text =
|
||||
// Column::with_children(stroke_text).spacing(26);
|
||||
|
||||
//Next
|
||||
let text_container = Container::new(text)
|
||||
.center(Length::Fill)
|
||||
.align_x(Horizontal::Left);
|
||||
let text_container = if delegate {
|
||||
// text widget based
|
||||
let font_size =
|
||||
scale_font(slide.font_size() as f32, width);
|
||||
let lines = slide_text.lines();
|
||||
let text: Vec<Element<Message>> = lines
|
||||
.map(|t| {
|
||||
rich_text([span(format!("{}\n", t))
|
||||
.background(
|
||||
Background::Color(Color::BLACK)
|
||||
.scale_alpha(0.4),
|
||||
)
|
||||
.border(border::rounded(10))
|
||||
.padding(10)])
|
||||
.size(font_size)
|
||||
.font(font)
|
||||
.center()
|
||||
.into()
|
||||
// let chars: Vec<Span> = t
|
||||
// .chars()
|
||||
// .map(|c| -> Span {
|
||||
// let character: String = format!("{}/n", c);
|
||||
// span(character)
|
||||
// .size(font_size)
|
||||
// .font(font)
|
||||
// .background(
|
||||
// Background::Color(Color::BLACK)
|
||||
// .scale_alpha(0.4),
|
||||
// )
|
||||
// .border(border::rounded(10))
|
||||
// .padding(10)
|
||||
})
|
||||
.collect();
|
||||
let text = Column::with_children(text).spacing(26);
|
||||
Container::new(text)
|
||||
.center(Length::Fill)
|
||||
.align_x(Horizontal::Left)
|
||||
} else {
|
||||
// SVG based
|
||||
let text = slide.text_svg.view().map(|m| Message::None);
|
||||
Container::new(text)
|
||||
.center(Length::Fill)
|
||||
.align_x(Horizontal::Left)
|
||||
// text widget based
|
||||
// let font_size =
|
||||
// scale_font(slide.font_size() as f32, width);
|
||||
// let lines = slide_text.lines();
|
||||
// let text: Vec<Element<Message>> = lines
|
||||
// .map(|t| {
|
||||
// rich_text([span(format!("{}\n", t))
|
||||
// .background(
|
||||
// Background::Color(Color::BLACK)
|
||||
// .scale_alpha(0.4),
|
||||
// )
|
||||
// .border(border::rounded(10))
|
||||
// .padding(10)])
|
||||
// .size(font_size)
|
||||
// .font(font)
|
||||
// .center()
|
||||
// .into()
|
||||
// // let chars: Vec<Span> = t
|
||||
// // .chars()
|
||||
// // .map(|c| -> Span {
|
||||
// // let character: String = format!("{}/n", c);
|
||||
// // span(character)
|
||||
// // .size(font_size)
|
||||
// // .font(font)
|
||||
// // .background(
|
||||
// // Background::Color(Color::BLACK)
|
||||
// // .scale_alpha(0.4),
|
||||
// // )
|
||||
// // .border(border::rounded(10))
|
||||
// // .padding(10)
|
||||
// })
|
||||
// .collect();
|
||||
// let text = Column::with_children(text).spacing(26);
|
||||
// Container::new(text)
|
||||
// .center(Length::Fill)
|
||||
// .align_x(Horizontal::Left)
|
||||
};
|
||||
|
||||
// let stroke_text_container = Container::new(stroke_text)
|
||||
// .center(Length::Fill)
|
||||
|
@ -648,11 +664,10 @@ pub(crate) fn slide_view(
|
|||
Color::BLACK,
|
||||
))
|
||||
})
|
||||
.center_x(width)
|
||||
.center_y(size.height)
|
||||
.center(Length::Fill)
|
||||
.clip(true)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.width(width)
|
||||
.height(size.height)
|
||||
} else if let Some(video) = &video {
|
||||
Container::new(
|
||||
VideoPlayer::new(video)
|
||||
|
|
|
@ -7,7 +7,7 @@ use colors_transform::Rgb;
|
|||
use cosmic::{
|
||||
iced::{
|
||||
font::{Style, Weight},
|
||||
Length,
|
||||
Length, Size,
|
||||
},
|
||||
prelude::*,
|
||||
widget::{container, lazy, responsive, svg::Handle, Svg},
|
||||
|
@ -61,6 +61,15 @@ impl From<cosmic::font::Font> for Font {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<String> for Font {
|
||||
fn from(value: String) -> Self {
|
||||
Self {
|
||||
name: value,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Font {
|
||||
fn from(value: &str) -> Self {
|
||||
Self {
|
||||
|
@ -168,8 +177,9 @@ pub enum Message {
|
|||
}
|
||||
|
||||
impl TextSvg {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(text: impl Into<String>) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +216,7 @@ impl TextSvg {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn view<'a>(self) -> Element<'a, Message> {
|
||||
pub fn build(mut self) -> Self {
|
||||
let shadow = if let Some(shadow) = &self.shadow {
|
||||
format!("<filter id=\"shadow\"><feDropShadow dx=\"{}\" dy=\"{}\" stdDeviation=\"{}\" flood-color=\"{}\"/></filter>",
|
||||
shadow.offset_x,
|
||||
|
@ -224,38 +234,58 @@ impl TextSvg {
|
|||
} else {
|
||||
"".into()
|
||||
};
|
||||
container(
|
||||
responsive(move |s| {
|
||||
let total_lines = self.text.lines().count();
|
||||
let half_lines = (total_lines / 2) as f32;
|
||||
let middle_position = s.height / 2.0;
|
||||
let line_spacing = 10.0;
|
||||
let text_and_line_spacing = self.font.size as f32 + line_spacing;
|
||||
let starting_y_position = middle_position - (half_lines * text_and_line_spacing);
|
||||
let size = Size::new(640.0, 360.0);
|
||||
let total_lines = self.text.lines().count();
|
||||
let half_lines = (total_lines / 2) as f32;
|
||||
let middle_position = size.height / 2.0;
|
||||
let line_spacing = 10.0;
|
||||
let text_and_line_spacing =
|
||||
self.font.size as f32 + line_spacing;
|
||||
let starting_y_position =
|
||||
middle_position - (half_lines * text_and_line_spacing);
|
||||
|
||||
let text_pieces: Vec<String> = self.text.lines()
|
||||
.enumerate()
|
||||
.map(|(index, text)| {
|
||||
format!("<tspan x=\"50%\" y=\"{}\">{}</tspan>", starting_y_position + (index as f32 * text_and_line_spacing), text)
|
||||
}).collect();
|
||||
let text: String = text_pieces.join("\n");
|
||||
let text_pieces: Vec<String> = self
|
||||
.text
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(index, text)| {
|
||||
format!(
|
||||
"<tspan x=\"50%\" y=\"{}\">{}</tspan>",
|
||||
starting_y_position
|
||||
+ (index as f32 * text_and_line_spacing),
|
||||
text
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let text: String = text_pieces.join("\n");
|
||||
|
||||
let final_svg = format!("<svg viewBox=\"0 0 {} {}\" xmlns=\"http://www.w3.org/2000/svg\"><defs>{}</defs><text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" font-weight=\"bold\" font-family=\"{}\" font-size=\"{}\" fill=\"{}\" {} style=\"filter:url(#shadow);\">{}</text></svg>",
|
||||
s.width,
|
||||
s.height,
|
||||
let final_svg = format!("<svg viewBox=\"0 0 {} {}\" xmlns=\"http://www.w3.org/2000/svg\"><defs>{}</defs><text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" font-weight=\"bold\" font-family=\"{}\" font-size=\"{}\" fill=\"{}\" {} style=\"filter:url(#shadow);\">{}</text></svg>",
|
||||
size.width,
|
||||
size.height,
|
||||
shadow,
|
||||
self.font.name,
|
||||
self.font.size,
|
||||
self.fill, stroke, text);
|
||||
let handle = Handle::from_memory(
|
||||
Box::leak(
|
||||
<std::string::String as Clone>::clone(&final_svg)
|
||||
.into_boxed_str(),
|
||||
)
|
||||
.as_bytes(),
|
||||
);
|
||||
self.handle = Some(handle);
|
||||
self
|
||||
}
|
||||
|
||||
// debug!(final_svg);
|
||||
Svg::new(Handle::from_memory(
|
||||
Box::leak(<std::string::String as Clone>::clone(&final_svg).into_boxed_str()).as_bytes(),
|
||||
))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
})).width(Length::Fill).height(Length::Fill).into()
|
||||
pub fn view<'a>(&self) -> Element<'a, Message> {
|
||||
container(
|
||||
Svg::new(self.handle.clone().unwrap())
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn text_spans(&self) -> Vec<String> {
|
||||
|
|
1
src/ui/widgets/mod.rs
Normal file
1
src/ui/widgets/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
// pub mod slide_text;
|
|
@ -0,0 +1,116 @@
|
|||
use cosmic::iced::advanced::layout::{self, Layout};
|
||||
use cosmic::iced::advanced::renderer;
|
||||
use cosmic::iced::advanced::widget::{self, Widget};
|
||||
use cosmic::iced::border;
|
||||
use cosmic::iced::mouse;
|
||||
use cosmic::iced::{Color, Element, Length, Rectangle, Size};
|
||||
use femtovg::renderer::WGPURenderer;
|
||||
use femtovg::{Canvas, TextContext};
|
||||
|
||||
pub struct SlideText {
|
||||
text: String,
|
||||
font_size: f32,
|
||||
canvas: Canvas<WGPURenderer>,
|
||||
}
|
||||
|
||||
impl SlideText {
|
||||
pub async fn new(text: &str) -> Self {
|
||||
let backends = wgpu::Backends::PRIMARY;
|
||||
let instance =
|
||||
wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
backends,
|
||||
..Default::default()
|
||||
});
|
||||
let surface =
|
||||
instance.create_surface(window.clone()).unwrap();
|
||||
let adapter = cosmic::iced::wgpu::util::initialize_adapter_from_env_or_default(&instance, Some(&surface))
|
||||
.await
|
||||
.expect("Failed to find an appropriate adapter");
|
||||
let (device, queue) = adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
required_features: adapter.features(),
|
||||
required_limits: wgpu::Limits::default(),
|
||||
memory_hints: wgpu::MemoryHints::Performance,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("failed to device it");
|
||||
let renderer = WGPURenderer::new(device, queue);
|
||||
let canvas =
|
||||
Canvas::new_with_text_context(renderer, text_context)
|
||||
.expect("oops femtovg");
|
||||
Self {
|
||||
text: text.to_owned(),
|
||||
font_size: 50.0,
|
||||
canvas,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_canvas(text_context: TextContext) -> Canvas {
|
||||
let renderer = WGPURenderer::new(device, queue);
|
||||
Canvas::new_with_text_context(renderer, text_context)
|
||||
.expect("oops femtovg")
|
||||
}
|
||||
|
||||
pub fn slide_text(text: &str) -> SlideText {
|
||||
SlideText::new(text)
|
||||
}
|
||||
|
||||
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||
for SlideText
|
||||
where
|
||||
Renderer: renderer::Renderer,
|
||||
{
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size {
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
_tree: &mut widget::Tree,
|
||||
_renderer: &Renderer,
|
||||
_limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
layout::Node::new(Size::new(
|
||||
self.font_size * 2.0,
|
||||
self.font_size * 2.0,
|
||||
))
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &widget::Tree,
|
||||
renderer: &mut Renderer,
|
||||
_theme: &Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
_cursor: mouse::Cursor,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: layout.bounds(),
|
||||
border: border::rounded(self.font_size),
|
||||
..renderer::Quad::default()
|
||||
},
|
||||
Color::BLACK,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message, Theme, Renderer> From<SlideText>
|
||||
for Element<'_, Message, Theme, Renderer>
|
||||
where
|
||||
Renderer: renderer::Renderer,
|
||||
{
|
||||
fn from(circle: SlideText) -> Self {
|
||||
Self::new(circle)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue