use crate::*;
use ordered_float::OrderedFloat;
use std::{borrow::Cow, f32, hash::*};
#[derive(Debug, Clone, PartialEq)]
pub struct Section<'a, X = Extra> {
pub screen_position: (f32, f32),
pub bounds: (f32, f32),
pub layout: Layout<BuiltInLineBreaker>,
pub text: Vec<Text<'a, X>>,
}
impl<X: Clone> Section<'_, X> {
#[inline]
pub(crate) fn clone_extras(&self) -> Vec<X> {
self.text.iter().map(|t| &t.extra).cloned().collect()
}
}
impl Default for Section<'static, Extra> {
#[inline]
fn default() -> Self {
Section::new()
}
}
impl<'a, X> Section<'a, X> {
#[inline]
pub fn new() -> Self {
Section::builder().with_text(vec![])
}
}
impl Section<'_, ()> {
#[inline]
pub fn builder() -> SectionBuilder {
<_>::default()
}
}
impl<'a, X> From<Text<'a, X>> for Section<'a, X> {
#[inline]
fn from(text: Text<'a, X>) -> Self {
Section::builder().add_text(text)
}
}
impl<'a, X> From<Vec<Text<'a, X>>> for Section<'a, X> {
#[inline]
fn from(text: Vec<Text<'a, X>>) -> Self {
Section::builder().with_text(text)
}
}
impl<'a, X> Section<'a, X> {
#[inline]
pub fn with_screen_position<P: Into<(f32, f32)>>(mut self, position: P) -> Self {
self.screen_position = position.into();
self
}
#[inline]
pub fn with_bounds<P: Into<(f32, f32)>>(mut self, bounds: P) -> Self {
self.bounds = bounds.into();
self
}
#[inline]
pub fn with_layout<L: Into<Layout<BuiltInLineBreaker>>>(mut self, layout: L) -> Self {
self.layout = layout.into();
self
}
#[inline]
pub fn add_text<T: Into<Text<'a, X>>>(mut self, text: T) -> Self {
self.text.push(text.into());
self
}
#[inline]
pub fn with_text<X2>(self, text: Vec<Text<'_, X2>>) -> Section<'_, X2> {
Section {
text,
screen_position: self.screen_position,
bounds: self.bounds,
layout: self.layout,
}
}
}
impl<'a, X: Clone> From<Section<'a, X>> for Cow<'a, Section<'a, X>> {
#[inline]
fn from(owned: Section<'a, X>) -> Self {
Cow::Owned(owned)
}
}
impl<'a, 'b, X: Clone> From<&'b Section<'a, X>> for Cow<'b, Section<'a, X>> {
#[inline]
fn from(owned: &'b Section<'a, X>) -> Self {
Cow::Borrowed(owned)
}
}
impl<X: Hash> Hash for Section<'_, X> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
let Section {
screen_position: (screen_x, screen_y),
bounds: (bound_w, bound_h),
layout,
ref text,
} = *self;
let ord_floats: &[OrderedFloat<_>] = &[
screen_x.into(),
screen_y.into(),
bound_w.into(),
bound_h.into(),
];
layout.hash(state);
hash_section_text(state, text);
ord_floats.hash(state);
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Text<'a, X = Extra> {
pub text: &'a str,
pub scale: PxScale,
pub font_id: FontId,
pub extra: X,
}
impl<X: Default> Default for Text<'static, X> {
#[inline]
fn default() -> Self {
Self {
text: "",
scale: PxScale::from(16.0),
font_id: <_>::default(),
extra: <_>::default(),
}
}
}
impl<'a, X> Text<'a, X> {
#[inline]
pub fn with_text(self, text: &str) -> Text<'_, X> {
Text {
text,
scale: self.scale,
font_id: self.font_id,
extra: self.extra,
}
}
#[inline]
pub fn with_scale<S: Into<PxScale>>(mut self, scale: S) -> Self {
self.scale = scale.into();
self
}
#[inline]
pub fn with_font_id<F: Into<FontId>>(mut self, font_id: F) -> Self {
self.font_id = font_id.into();
self
}
#[inline]
pub fn with_extra<X2>(self, extra: X2) -> Text<'a, X2> {
Text {
text: self.text,
scale: self.scale,
font_id: self.font_id,
extra,
}
}
}
impl<'a, X: Default> Text<'a, X> {
#[inline]
pub fn new(text: &'a str) -> Self {
Text::default().with_text(text)
}
}
impl<'a> Text<'a, Extra> {
#[inline]
pub fn with_color<C: Into<Color>>(mut self, color: C) -> Self {
self.extra.color = color.into();
self
}
#[inline]
pub fn with_z<Z: Into<f32>>(mut self, z: Z) -> Self {
self.extra.z = z.into();
self
}
}
impl<X> ToSectionText for Text<'_, X> {
#[inline]
fn to_section_text(&self) -> SectionText<'_> {
SectionText {
text: self.text,
scale: self.scale,
font_id: self.font_id,
}
}
}
#[inline]
fn hash_section_text<X: Hash, H: Hasher>(state: &mut H, text: &[Text<'_, X>]) {
for t in text {
let Text {
text,
scale,
font_id,
ref extra,
} = *t;
let ord_floats: [OrderedFloat<_>; 2] = [scale.x.into(), scale.y.into()];
(text, font_id, extra, ord_floats).hash(state);
}
}
impl<'text, X: Clone> Section<'text, X> {
pub fn to_owned(&self) -> OwnedSection<X> {
OwnedSection {
screen_position: self.screen_position,
bounds: self.bounds,
layout: self.layout,
text: self.text.iter().map(OwnedText::from).collect(),
}
}
#[inline]
pub(crate) fn to_hashable_parts(&self) -> HashableSectionParts<'_, X> {
let Section {
screen_position: (screen_x, screen_y),
bounds: (bound_w, bound_h),
ref text,
layout: _,
} = *self;
let geometry = [
screen_x.into(),
screen_y.into(),
bound_w.into(),
bound_h.into(),
];
HashableSectionParts { geometry, text }
}
}
impl<X> From<&Section<'_, X>> for SectionGeometry {
#[inline]
fn from(section: &Section<'_, X>) -> Self {
Self {
bounds: section.bounds,
screen_position: section.screen_position,
}
}
}
pub(crate) struct HashableSectionParts<'a, X> {
geometry: [OrderedFloat<f32>; 4],
text: &'a [Text<'a, X>],
}
impl<X: Hash> HashableSectionParts<'_, X> {
#[inline]
pub fn hash_geometry<H: Hasher>(&self, state: &mut H) {
self.geometry.hash(state);
}
#[inline]
pub fn hash_text_no_extra<H: Hasher>(&self, state: &mut H) {
for t in self.text {
let Text {
text,
scale,
font_id,
..
} = *t;
let ord_floats: &[OrderedFloat<_>] = &[scale.x.into(), scale.y.into()];
(text, font_id, ord_floats).hash(state);
}
}
#[inline]
pub fn hash_extra<H: Hasher>(&self, state: &mut H) {
self.text.iter().for_each(|t| t.extra.hash(state));
}
}