4 | 4 |
use failure;
|
5 | 5 |
|
6 | 6 |
use pango::LayoutExt;
|
| 7 |
pub use pango::Weight;
|
7 | 8 |
|
| 9 |
/// A `Font` is a more convenient, less imperative abstraction over
|
| 10 |
/// the Pango `FontDescription` type
|
| 11 |
pub struct Font {
|
| 12 |
f: pango::FontDescription,
|
| 13 |
}
|
| 14 |
|
| 15 |
impl Font {
|
| 16 |
/// Create a new `Font` from a textual description of the font
|
| 17 |
pub fn new(s: &str) -> Font {
|
| 18 |
Font { f: pango::FontDescription::from_string(s) }
|
| 19 |
}
|
| 20 |
|
| 21 |
/// Set the font size in points
|
| 22 |
pub fn size(mut self, s: i32) -> Font {
|
| 23 |
self.f.set_size(s * pango::SCALE);
|
| 24 |
self
|
| 25 |
}
|
| 26 |
|
| 27 |
/// Set the font weight
|
| 28 |
pub fn weight(mut self, w: pango::Weight) -> Font {
|
| 29 |
self.f.set_weight(w);
|
| 30 |
self
|
| 31 |
}
|
| 32 |
|
| 33 |
/// Get the underlying pango `FontDescription` value
|
| 34 |
pub fn get(self) -> pango::FontDescription {
|
| 35 |
self.f
|
| 36 |
}
|
| 37 |
}
|
| 38 |
|
| 39 |
/// A `MarkupBuffer` is an abstraction that allows a high-level
|
| 40 |
/// mostly-correct-by-construction (in theory, anyway) approach to
|
| 41 |
/// adding Pango-level formatting
|
8 | 42 |
pub struct MarkupBuffer {
|
9 | 43 |
buf: String,
|
10 | 44 |
}
|
11 | 45 |
|
12 | |
pub fn title_font() -> pango::FontDescription {
|
13 | |
let mut f = pango::FontDescription::from_string("Fira Sans 12");
|
14 | |
f.set_weight(pango::Weight::Bold);
|
15 | |
f
|
16 | |
}
|
17 | |
|
18 | |
pub fn body_font() -> pango::FontDescription {
|
19 | |
pango::FontDescription::from_string("Fira Sans 10")
|
20 | |
}
|
21 | |
|
22 | 46 |
impl MarkupBuffer {
|
| 47 |
/// Create a new empty `MarkupBuffer`
|
23 | 48 |
pub fn new() -> MarkupBuffer {
|
24 | 49 |
MarkupBuffer { buf: String::new() }
|
25 | 50 |
}
|
26 | 51 |
|
| 52 |
/// Create a view on the `MarkupBuffer` that permits markup of the
|
| 53 |
/// added strings
|
27 | 54 |
pub fn markup<'a>(&'a mut self) -> Markup<'a> {
|
28 | 55 |
Markup {
|
29 | 56 |
view: self,
|
|
31 | 58 |
}
|
32 | 59 |
}
|
33 | 60 |
|
| 61 |
/// Add a string fragment to the markup buffer
|
34 | 62 |
pub fn push(&mut self, s: &str) {
|
35 | 63 |
self.buf.push_str(s);
|
36 | 64 |
}
|
37 | 65 |
|
| 66 |
/// Add a single space to the buffer
|
38 | 67 |
pub fn space(&mut self) {
|
39 | 68 |
self.buf.push_str(" ");
|
40 | 69 |
}
|
41 | 70 |
|
42 | |
pub fn get(self) -> String {
|
| 71 |
/// Get the underlying string value of the buffer
|
| 72 |
fn get(self) -> String {
|
43 | 73 |
self.buf
|
44 | 74 |
}
|
45 | 75 |
|
| 76 |
/// Draw the buffer to the underlying Cairo surface, with an
|
| 77 |
/// optional width for layout purposes
|
46 | 78 |
pub fn show_with_font(
|
47 | 79 |
self,
|
48 | 80 |
ctx: &cairo::Context,
|
49 | |
font: pango::FontDescription,
|
| 81 |
font: Font,
|
50 | 82 |
width: Option<i32>,
|
51 | 83 |
) -> Result<(), failure::Error> {
|
52 | 84 |
let layout = pangocairo::functions::create_layout(ctx)
|
53 | 85 |
.ok_or(format_err!("Unable to create Pango layout"))?;
|
54 | |
layout.set_font_description(Some(&font));
|
| 86 |
layout.set_font_description(Some(&font.get()));
|
55 | 87 |
if let Some(w) = width {
|
56 | 88 |
layout.set_width(w * pango::SCALE);
|
57 | 89 |
}
|
|
65 | 97 |
}
|
66 | 98 |
|
67 | 99 |
enum Frag {
|
68 | |
Weight(String),
|
| 100 |
Weight(pango::Weight),
|
69 | 101 |
Size(u8),
|
70 | 102 |
}
|
71 | 103 |
|
| 104 |
/// A `Markup` value is a view into a `MarkupBuffer` that permits
|
| 105 |
/// extra markup to be applied transparently.
|
72 | 106 |
pub struct Markup<'a> {
|
73 | 107 |
view: &'a mut MarkupBuffer,
|
74 | 108 |
frags: Vec<Frag>,
|
75 | 109 |
}
|
76 | 110 |
|
77 | 111 |
impl<'a> Markup<'a> {
|
78 | |
pub fn weight(self, s: &str) -> Markup<'a> {
|
| 112 |
/// Give this view a different weight than usual
|
| 113 |
pub fn weight(self, w: pango::Weight) -> Markup<'a> {
|
79 | 114 |
let Markup { view, mut frags } = self;
|
80 | |
frags.push(Frag::Weight(s.to_owned()));
|
| 115 |
frags.push(Frag::Weight(w));
|
81 | 116 |
Markup { view, frags }
|
82 | 117 |
}
|
83 | 118 |
|
| 119 |
/// Set the font size in points for the view
|
84 | 120 |
pub fn size(self, s: u8) -> Markup<'a> {
|
85 | 121 |
let Markup { view, mut frags } = self;
|
86 | 122 |
frags.push(Frag::Size(s));
|
87 | 123 |
Markup { view, frags }
|
88 | 124 |
}
|
89 | 125 |
|
| 126 |
/// Add a string with the extra markup supplied
|
90 | 127 |
pub fn push(self, s: &str) {
|
91 | 128 |
self.view.push("<span ");
|
92 | 129 |
for f in self.frags.iter() {
|
93 | 130 |
match f {
|
94 | 131 |
&Frag::Size(s) =>
|
95 | 132 |
self.view.push(&format!("font='{}' ", s)),
|
96 | |
&Frag::Weight(ref s) =>
|
97 | |
self.view.push(&format!("font_weight='{}' ", s)),
|
| 133 |
&Frag::Weight(s) =>
|
| 134 |
self.view.push(&format!(
|
| 135 |
"font_weight='{}' ",
|
| 136 |
weight_to_string(s),
|
| 137 |
)),
|
98 | 138 |
}
|
99 | 139 |
}
|
100 | 140 |
self.view.push(">");
|
|
102 | 142 |
self.view.push("</span>");
|
103 | 143 |
}
|
104 | 144 |
}
|
| 145 |
|
| 146 |
fn weight_to_string(w: Weight) -> &'static str {
|
| 147 |
match w {
|
| 148 |
Weight::Thin => "thin",
|
| 149 |
Weight::Ultralight => "ultralight",
|
| 150 |
Weight::Light => "light",
|
| 151 |
Weight::Semilight => "semilight",
|
| 152 |
Weight::Book => "book",
|
| 153 |
Weight::Normal => "normal",
|
| 154 |
Weight::Medium => "medium",
|
| 155 |
Weight::Semibold => "semibold",
|
| 156 |
Weight::Bold => "bold",
|
| 157 |
Weight::Ultrabold => "ultrabold",
|
| 158 |
Weight::Heavy => "heavy",
|
| 159 |
Weight::Ultraheavy => "ultraheavy",
|
| 160 |
_ => panic!("Unknown weight: {:?}", w),
|
| 161 |
}
|
| 162 |
}
|