making views in presenter.rs more composable

This commit is contained in:
Chris Cochrun 2024-12-17 15:22:46 -06:00
parent f98dc599f9
commit e998d0778c

View file

@ -31,6 +31,9 @@ use crate::{
BackgroundKind, BackgroundKind,
}; };
const REFERENCE_WIDTH: f32 = 1920.0;
const REFERENCE_HEIGHT: f32 = 1080.0;
// #[derive(Default, Clone, Debug)] // #[derive(Default, Clone, Debug)]
pub(crate) struct Presenter { pub(crate) struct Presenter {
pub slides: Vec<Slide>, pub slides: Vec<Slide>,
@ -363,159 +366,21 @@ impl Presenter {
} }
pub fn view(&self) -> Element<Message> { pub fn view(&self) -> Element<Message> {
responsive(|size| { self.slide_view(
let font = self.current_font; &self.current_slide,
let font_size = if self.current_slide.font_size() > 0 { self.current_font,
(size.width / self.current_slide.font_size() as f32) false,
* 3.0 true,
} else { )
50.0
};
let slide_text = self.current_slide.text();
let lines = slide_text.lines();
let line_size = lines.clone().count();
// debug!(?lines);
let text: Vec<Element<Message>> = lines
.map(|t| {
text(t.to_string())
.size(font_size)
.font(font)
.width(Length::Fill)
.align_x(Horizontal::Center)
.into()
})
.collect();
let texts = Column::with_children(text);
// let text = text(self.current_slide.text())
// .size(font_size)
// .font(font)
// .align_x(Horizontal::Center);
let text = Container::new(texts).center(Length::Fill);
let text_background = Container::new(Space::new(0, 0))
.style(|_| {
container::background(
Background::Color(Color::BLACK)
.scale_alpha(0.3),
)
})
.width(size.width)
.height(
font_size * line_size as f32 * line_size as f32,
);
let text_stack = stack!(text_background, text);
let text_container =
Container::new(text_stack).center(Length::Fill);
let black = Container::new(Space::new(0, 0))
.style(|_| {
container::background(Background::Color(
Color::BLACK,
))
})
.width(size.width)
.height(size.height);
let container = match self.current_slide.background().kind
{
BackgroundKind::Image => {
let path =
self.current_slide.background().path.clone();
Container::new(
image(path)
.content_fit(ContentFit::Cover)
.width(size.width)
.height(size.height),
)
}
BackgroundKind::Video => {
if let Some(video) = &self.video {
Container::new(
VideoPlayer::new(video)
.mouse_hidden(true)
.width(size.width)
.height(size.width * 9.0 / 16.0)
.on_end_of_stream(Message::EndVideo)
.on_new_frame(Message::VideoFrame)
.on_missing_plugin(
Message::MissingPlugin,
)
.on_warning(|w| {
Message::Error(w.to_string())
})
.on_error(|e| {
Message::Error(e.to_string())
})
.content_fit(ContentFit::Cover),
)
} else {
Container::new(Space::new(0, 0))
}
}
};
stack!(
black,
container.center(Length::Fill),
text_container
)
.width(Length::Fill)
.height(Length::Fill)
.into()
})
.into()
} }
pub fn view_preview(&self) -> Element<Message> { pub fn view_preview(&self) -> Element<Message> {
responsive(|size| { self.slide_view(
let font = self.current_font; &self.current_slide,
let font_size = if self.current_slide.font_size() > 0 { self.current_font,
(size.width / self.current_slide.font_size() as f32) false,
* 3.0 false,
} else { )
50.0
};
let text = text(self.current_slide.text())
.size(font_size)
.font(font);
let text = Container::new(text).center(Length::Fill);
let black = Container::new(Space::new(0, 0))
.style(|_| {
container::background(Background::Color(
Color::BLACK,
))
})
.width(size.width)
.height(size.height);
let container = match self.current_slide.background().kind
{
BackgroundKind::Image => {
let path =
self.current_slide.background().path.clone();
Container::new(
image(path)
.content_fit(ContentFit::Cover)
.width(size.width)
.height(size.height),
)
}
BackgroundKind::Video => {
if let Some(video) = &self.video {
Container::new(
VideoPlayer::new(video)
.width(size.width)
.height(size.width * 9.0 / 16.0)
.on_end_of_stream(Message::EndVideo)
.on_new_frame(Message::VideoFrame)
.content_fit(ContentFit::Cover),
)
} else {
Container::new(Space::new(0, 0))
}
}
};
stack!(black, container.center(Length::Fill), text)
.width(size.width)
.height(size.width * 9.0 / 16.0)
.into()
})
.into()
} }
pub fn preview_bar(&self) -> Element<Message> { pub fn preview_bar(&self) -> Element<Message> {
@ -547,86 +412,59 @@ impl Presenter {
stretch, stretch,
style, style,
}; };
let slide_id = let slide_id =
self.slides.iter().position(|s| s == slide).unwrap() self.slides.iter().position(|s| s == slide).unwrap()
as i32; as i32;
let text = Responsive::new(move |size| {
let font_size = if slide.font_size() > 0 {
(size.width / slide.font_size() as f32) * 3.0
} else {
50.0
};
let text = text(slide.text()).size(font_size).font(font);
let text = Container::new(text).center(Length::Fill);
text.into()
});
let container = match slide.background().kind {
BackgroundKind::Image => {
let path = slide.background().path.clone();
Container::new( let container = self.slide_view(slide, font, true, false);
image(path)
.content_fit(ContentFit::Contain)
.border_radius([10.0; 4]),
)
}
BackgroundKind::Video => Container::new(Space::new(0, 0))
.style(|_| {
container::background(Background::Color(
Color::BLACK,
))
})
.width(Length::Fill)
.height(Length::Fill),
};
let delegate = mouse_area( let delegate = mouse_area(
Container::new( Container::new(container)
stack!(container.center(Length::Fill), text) .style(move |t| {
.width(Length::Fill) let mut style = container::Style::default();
.height(Length::Fill), let theme = t.cosmic();
) let hovered = self.hovered_slide == slide_id;
.style(move |t| { style.background = Some(Background::Color(
let mut style = container::Style::default();
let theme = t.cosmic();
let hovered = self.hovered_slide == slide_id;
style.background = Some(Background::Color(
if self.current_slide_index as i32 == slide_id {
theme.accent.base.into()
} else if hovered {
theme.accent.hover.into()
} else {
theme.palette.neutral_3.into()
},
));
style.border = Border::default().rounded(10.0);
style.shadow = Shadow {
color: Color::BLACK,
offset: {
if self.current_slide_index as i32 == slide_id if self.current_slide_index as i32 == slide_id
{ {
Vector::new(5.0, 5.0) theme.accent.base.into()
} else if hovered { } else if hovered {
Vector::new(5.0, 5.0) theme.accent.hover.into()
} else { } else {
Vector::new(0.0, 0.0) theme.palette.neutral_3.into()
} },
}, ));
blur_radius: { style.border = Border::default().rounded(10.0);
if self.current_slide_index as i32 == slide_id style.shadow = Shadow {
{ color: Color::BLACK,
10.0 offset: {
} else if hovered { if self.current_slide_index as i32
10.0 == slide_id
} else { {
0.0 Vector::new(5.0, 5.0)
} } else if hovered {
}, Vector::new(5.0, 5.0)
}; } else {
style Vector::new(0.0, 0.0)
}) }
.center_x(100.0 * 16.0 / 9.0) },
.height(100) blur_radius: {
.padding(10), if self.current_slide_index as i32
== slide_id
{
10.0
} else if hovered {
10.0
} else {
0.0
}
},
};
style
})
.center_x(100.0 * 16.0 / 9.0)
.height(100)
.padding(10),
) )
.interaction(cosmic::iced::mouse::Interaction::Pointer) .interaction(cosmic::iced::mouse::Interaction::Pointer)
.on_enter(Message::HoveredSlide(slide_id)) .on_enter(Message::HoveredSlide(slide_id))
@ -639,6 +477,117 @@ impl Presenter {
delegate.into() delegate.into()
} }
fn slide_view<'a>(
&'a self,
slide: &'a Slide,
font: Font,
delegate: bool,
hide_mouse: bool,
) -> Element<'a, Message> {
responsive(move |size| {
let font_size = scale_font(
slide.font_size() as f32,
size.width,
size.height,
);
let slide_text = slide.text();
let lines = slide_text.lines();
let line_size = lines.clone().count();
// debug!(?lines);
let text: Vec<Element<Message>> = lines
.map(|t| {
text(t.to_string())
.size(font_size)
.font(font)
.width(Length::Fill)
.align_x(Horizontal::Center)
.into()
})
.collect();
let texts = Column::with_children(text);
// let text = text(slide.text())
// .size(font_size)
// .font(font)
// .align_x(Horizontal::Center);
let text = Container::new(texts).center(Length::Fill);
let text_background = Container::new(Space::new(0, 0))
.style(|_| {
container::background(
Background::Color(Color::BLACK)
.scale_alpha(0.3),
)
})
.width(size.width)
.height(
font_size * line_size as f32 * line_size as f32,
);
let text_stack = stack!(text_background, text);
let text_container =
Container::new(text_stack).center(Length::Fill);
let black = Container::new(Space::new(0, 0))
.style(|_| {
container::background(Background::Color(
Color::BLACK,
))
})
.width(size.width)
.height(size.height);
let container = match slide.background().kind {
BackgroundKind::Image => {
let path = slide.background().path.clone();
Container::new(
image(path)
.content_fit(ContentFit::Cover)
.width(size.width)
.height(size.height),
)
}
BackgroundKind::Video => {
if delegate {
Container::new(Space::new(0, 0))
.style(|_| {
container::background(
Background::Color(Color::BLACK),
)
})
.width(Length::Fill)
.height(Length::Fill)
} else if let Some(video) = &self.video {
Container::new(
VideoPlayer::new(video)
.mouse_hidden(hide_mouse)
.width(size.width)
.height(size.width * 9.0 / 16.0)
.on_end_of_stream(Message::EndVideo)
.on_new_frame(Message::VideoFrame)
.on_missing_plugin(
Message::MissingPlugin,
)
.on_warning(|w| {
Message::Error(w.to_string())
})
.on_error(|e| {
Message::Error(e.to_string())
})
.content_fit(ContentFit::Cover),
)
} else {
Container::new(Space::new(0, 0))
}
}
};
stack!(
black,
container.center(Length::Fill),
text_container
)
.width(Length::Fill)
.height(Length::Fill)
.into()
})
.into()
}
fn reset_video(&mut self) { fn reset_video(&mut self) {
if let Some(slide) = if let Some(slide) =
self.slides.get(self.current_slide_index as usize) self.slides.get(self.current_slide_index as usize)
@ -681,3 +630,16 @@ async fn start_audio(sink: Arc<Sink>, audio: PathBuf) {
let paused = sink.is_paused(); let paused = sink.is_paused();
debug!(empty, paused, "Finished running"); debug!(empty, paused, "Finished running");
} }
fn scale_font(font_size: f32, width: f32, height: f32) -> f32 {
let aspect_ratio_true = REFERENCE_WIDTH / REFERENCE_HEIGHT;
let aspect_ratio = width / height;
let scale_factor = (REFERENCE_WIDTH / width).sqrt();
// debug!(scale_factor);
let font_size = if font_size > 0.0 {
font_size / scale_factor
} else {
50.0
};
font_size
}