use crate::{
dictionary::Value as DataValue,
format::{Format, Settings as FormatSettings, Type, UncheckedFormat},
+ raw::VarType,
settings::{Settings, Show},
};
fn default_cell_style(self) -> CellStyle {
use HorzAlign::*;
use VertAlign::*;
- let (halign, valign, hmargins, vmargins) = match self {
- Area::Title => (Center, Middle, [8, 11], [1, 8]),
- Area::Caption => (Left, Top, [8, 11], [1, 1]),
- Area::Footer => (Left, Top, [11, 8], [2, 3]),
- Area::Corner => (Left, Bottom, [8, 11], [1, 1]),
- Area::ColumnLabels => (Left, Top, [8, 11], [1, 3]),
- Area::RowLabels => (Left, Top, [8, 11], [1, 3]),
- Area::Data => (Mixed, Top, [8, 11], [1, 1]),
- Area::Layers => (Left, Bottom, [8, 11], [1, 3]),
+ let (horz_align, vert_align, hmargins, vmargins) = match self {
+ Area::Title => (Some(Center), Middle, [8, 11], [1, 8]),
+ Area::Caption => (Some(Left), Top, [8, 11], [1, 1]),
+ Area::Footer => (Some(Left), Top, [11, 8], [2, 3]),
+ Area::Corner => (Some(Left), Bottom, [8, 11], [1, 1]),
+ Area::ColumnLabels => (Some(Left), Top, [8, 11], [1, 3]),
+ Area::RowLabels => (Some(Left), Top, [8, 11], [1, 3]),
+ Area::Data => (None, Top, [8, 11], [1, 1]),
+ Area::Layers => (Some(Left), Bottom, [8, 11], [1, 3]),
};
CellStyle {
- horz_align: halign,
- vert_align: valign,
+ horz_align,
+ vert_align,
margins: enum_map! { Axis2::X => hmargins, Axis2::Y => vmargins },
}
}
#[derive(Clone, Debug)]
pub struct CellStyle {
- pub horz_align: HorzAlign,
+ /// `None` means "mixed" alignment: align strings to the left, numbers to
+ /// the right.
+ pub horz_align: Option<HorzAlign>,
pub vert_align: VertAlign,
/// Margins in 1/96" units.
/// Centered.
Center,
- /// Align strings to the left, other formats to the right.
- Mixed,
-
/// Align the decimal point at the specified position.
Decimal {
/// Decimal offset from the right side of the cell, in 1/96" units.
},
}
+impl HorzAlign {
+ fn for_mixed(var_type: VarType) -> Self {
+ match var_type {
+ VarType::Numeric => Self::Right,
+ VarType::String => Self::Left,
+ }
+ }
+}
+
#[derive(Copy, Clone, Debug)]
pub enum VertAlign {
/// Top alignment.
subscripts: &'a [String],
footnotes: &'a [Arc<Footnote>],
options: ValueOptions,
+ show_value: bool,
+ show_label: Option<&'a str>,
}
impl<'a> DisplayValue<'a> {
Self { footnotes, ..self }
}
- fn show(&self) -> (bool, Option<&str>) {
- if let Some(value_label) = self.inner.value_label() {
- interpret_show(
- || Settings::global().show_values,
- self.options.show_values,
- self.inner.show(),
- value_label,
- )
- } else if let Some(variable_label) = self.inner.variable_label() {
- interpret_show(
- || Settings::global().show_variables,
- self.options.show_variables,
- self.inner.show(),
- variable_label,
- )
- } else {
- (true, None)
- }
- }
-
fn small(&self) -> f64 {
self.options.small
}
+ fn var_type(&self) -> VarType {
+ match self.inner {
+ ValueInner::Number { .. } if self.show_label.is_none() => VarType::Numeric,
+ _ => VarType::String,
+ }
+ }
+
fn template(
&self,
f: &mut std::fmt::Formatter<'_>,
impl<'a, 'b> Display for DisplayValue<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let (show_value, label) = self.show();
match self.inner {
ValueInner::Number {
format,
value,
..
} => {
- if show_value {
+ if self.show_value {
let format = if format.type_() == Type::F
&& *honor_small
&& value.is_some_and(|value| value != 0.0 && value.abs() < self.small())
.unwrap();
write!(f, "{}", buf.trim_start_matches(' '))?;
}
- if let Some(label) = label {
- if show_value {
+ if let Some(label) = self.show_label {
+ if self.show_value {
write!(f, " ")?;
}
f.write_str(label)?;
}
ValueInner::String { s, .. } | ValueInner::Variable { var_name: s, .. } => {
- match (show_value, label) {
+ match (self.show_value, self.show_label) {
(true, None) => write!(f, "{s}"),
(false, Some(label)) => write!(f, "{label}"),
(true, Some(label)) => write!(f, "{s} {label}"),
// Returns an object that will format this value. Settings on `options`
// control whether variable and value labels are included.
pub fn display<'a>(&'a self, options: impl AsValueOptions) -> DisplayValue<'a> {
+ let options = options.as_value_options();
+ let (show_value, show_label) = if let Some(value_label) = self.value_label() {
+ interpret_show(
+ || Settings::global().show_values,
+ options.show_values,
+ self.show(),
+ value_label,
+ )
+ } else if let Some(variable_label) = self.variable_label() {
+ interpret_show(
+ || Settings::global().show_variables,
+ options.show_variables,
+ self.show(),
+ variable_label,
+ )
+ } else {
+ (true, None)
+ };
DisplayValue {
inner: &self,
markup: false,
subscripts: &[],
footnotes: &[],
- options: options.as_value_options(),
+ options,
+ show_value,
+ show_label,
}
}
}
}
}
- fn layout_cell(&self, cell: &DrawCell, mut text: &str, bb: Rect2) -> Coord2 {
+ fn cell_to_text(cell: &DrawCell) -> String {
+ cell.inner
+ .display(() /* XXX */)
+ .with_font_style(&cell.style.font_style)
+ .with_subscripts(cell.subscripts)
+ .with_footnotes(cell.footnotes)
+ .to_string()
+ }
+
+ fn layout_cell(&self, cell: &DrawCell, mut text: &str, bb: Rect2, clip: Rect2) -> Coord2 {
if text.is_empty() {
return Coord2::default();
}
- let mut breaks = new_line_breaks(text, bb[Axis2::X].len());
+ use Axis2::*;
+ let mut breaks = new_line_breaks(text, bb[X].len());
let mut size = Coord2::new(0, 0);
- for text in breaks {
+ for (text, y) in breaks.zip(bb[Y].clone()) {
+ let width = text.width();
+ if width > size[X] {
+ size[X] = width;
+ }
+ size[Y] += 1;
+
+ if !clip[Y].contains(&y) {
+ continue;
+ }
+
}
todo!()
}
fn measure_cell_width(&self, cell: &DrawCell) -> [usize; 2] {
- let text = cell
- .inner
- .display(() /* XXX */)
- .with_font_style(&cell.style.font_style)
- .with_subscripts(cell.subscripts)
- .with_footnotes(cell.footnotes)
- .to_string();
- let max_width = self.layout_cell(cell, &text, Rect2::new(0..usize::MAX, 0..usize::MAX));
- let min_width = self.layout_cell(cell, &text, Rect2::new(0..1, 0..usize::MAX));
+ let text = Self::cell_to_text(cell);
+ let max_width = self.layout_cell(
+ cell,
+ &text,
+ Rect2::new(0..usize::MAX, 0..usize::MAX),
+ Rect2::default(),
+ );
+ let min_width = self.layout_cell(
+ cell,
+ &text,
+ Rect2::new(0..1, 0..usize::MAX),
+ Rect2::default(),
+ );
[min_width.x(), max_width.x()]
}
fn measure_cell_height(&self, cell: &DrawCell, width: usize) -> usize {
- todo!()
+ let text = Self::cell_to_text(cell);
+ self.layout_cell(
+ cell,
+ &text,
+ Rect2::new(0..width, 0..usize::MAX),
+ Rect2::default(),
+ )
+ .y()
}
fn adjust_break(&self, cell: &Content, size: Coord2) -> usize {