1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2020-2021 Ian Jackson and contributors to Otter
// SPDX-License-Identifier: AGPL-3.0-or-later
// There is NO WARRANTY.

use crate::prelude::*;

pub const HELD_SURROUND_COLOUR: &str = "black";

const MONOSPACE: HtmlLit = Html::lit(
  r#"font-family="DejaVu Sans Mono, monospace" font-weight="700""#);

const USVG_DEFAULT_ARGS_TEXT: &str = include_str!("USVG_DEFAULT_ARGS.txt");

pub const DEFAULT_TABLE_SIZE: Pos = PosC::new( 300, 200 );
pub const DEFAULT_TABLE_COLOUR: &str = "green";

pub const SELECT_SCALE: f64 = 1.1;

// also in script.ts:redisplay_ancillaries ("halo")
//   nelem.setAttributeNS(null,'stroke-width','2px');
pub const SELECT_STROKE_WIDTH: f64 = 2.0;

pub const DEFAULT_EDGE_WIDTH: f64 = 0.2;
pub const INVISIBLE_EDGE_SENSITIVE: f64 = 2.;

pub const LABEL_FONT_SIZE: f64 = 4.0;

pub const HTML_TEXT_LABEL_ELEM_START: HtmlLit =
  Html::lit(r##"text pointer-events="none""##);

#[derive(Debug,Serialize,Deserialize,Clone)]
pub struct TextOptions {
  pub colour: Colour,
  pub size: f64, // px
}

/// Fudge factor
///
/// When trying to centre text, we use text-align and/or text-anchor
/// to do the horizontal positioning, but vertical positioning is
/// troublesome.  We bodge it.  Multiple the font size (in pixels)
/// by this, and add it to the SVG y coordinate (ie, shufting the text
/// down).
pub const SVG_FONT_Y_ADJUST_OF_FONT_SIZE: f64 =
  include!("SVG_FONT_Y_ADJUST_OF_FONT_SIZE.txt");

pub fn default_edge_width() -> f64 { DEFAULT_EDGE_WIDTH }

pub fn monospace_font(size: u32) -> Html {
  hformat!(r##"{} font-size="{}""##, MONOSPACE, size)
}

#[derive(Clone,Copy,Debug,Eq,PartialEq,Serialize,Deserialize,EnumString)]
pub enum PresentationLayout {
  Portrait,
  Landscape,
}

type PL = PresentationLayout;

pub fn player_num_dasharray(player_num: NonZeroUsize) -> Html {
  let n: usize = player_num.into();
  let mut dasharray = String::with_capacity(n*3 + 4);
  for dash in iter::once("3").chain(
    iter::repeat("1").take(n-1))
  {
    write!(&mut dasharray, "{} 1 ", &dash).unwrap();
  }
  let spc = dasharray.pop();
  assert_eq!(spc,Some(' '));
  Html::from_html_string(dasharray)
}

pub fn player_dasharray(gplayers: &GPlayers, player: PlayerId) -> Html {
  let kd: slotmap::KeyData = player.into();
  let n: usize = kd.get_idx_version().0.try_into().unwrap();
  let n: NonZeroUsize = n.try_into()
    .unwrap_or_else(|_| gplayers.capacity().try_into().unwrap());
  player_num_dasharray(n)
}

pub fn occultation_notify_update_image(piece: PieceId)
                                       -> UnpreparedUpdates {
  vec![Box::new(
    move |updates: &mut PrepareUpdatesBuffer| {
      updates.piece_update_image(piece, &None)
        .unwrap_or_else(|e| error!("unable to send update! {:?}", e))
    }
  )]
}

impl PresentationLayout {
  pub fn template(self) -> &'static str {
    match self {
      PL::Portrait => "session",
      PL::Landscape => "landscape",
    }
  }
  pub fn abbreviate_timestamps(self) -> bool {
    match self {
      PL::Portrait => false,
      PL::Landscape => true,
    }
  }
}

impl Default for PresentationLayout {
  fn default() -> Self { PL::Portrait }
}

#[derive(Debug)]
pub struct AbbrevPresentationLayout(pub PresentationLayout);

#[derive(Error,Debug,Clone,Copy)]
#[error("Invalid presentation layout character")]
pub struct InvalidAbbrevPresentationLayout;

impl FromStr for AbbrevPresentationLayout {
  type Err = InvalidAbbrevPresentationLayout;
  #[throws(Self::Err)]
  fn from_str(s: &str) -> Self {
    AbbrevPresentationLayout(match s {
      "p" => PL::Portrait,
      "l" => PL::Landscape,
      _ => throw!(InvalidAbbrevPresentationLayout)
    })
  }
}

impl Display for AbbrevPresentationLayout {
  #[throws(fmt::Error)]
  fn fmt(&self, f: &mut fmt::Formatter) {
    f.write_str(match self.0 {
      PL::Portrait => "p",
      PL::Landscape => "l",
    })?
  }
}

impl TextOptions {
  pub fn y_adjust(&self) -> f64 {
    self.size * SVG_FONT_Y_ADJUST_OF_FONT_SIZE
  }

  pub fn start_element(&self) -> Html {
    hformat!{
 r##"{} text-align="center" text-anchor="middle" x="0" y="{}" fill="{}" font-size="{}px""##,
             HTML_TEXT_LABEL_ELEM_START,
             self.y_adjust(), &self.colour, self.size,
    }
  }
}

pub fn usvg_default_args() -> impl Iterator<Item=&'static str> {
  USVG_DEFAULT_ARGS_TEXT
    .lines()
    .map(|l| l.trim())
    .filter(|l| l.len() > 0 && ! l.starts_with('#'))
}