Implement Hacker News specific styles and drawing rules
This commit is contained in:
1 parent
ff622c00a0
commit
e3d072f0b7
4 files changed
+72
-21
No files matched your search
@@ -43,6 +43,9 @@ pub fn layout_dom(tree: &DomTree, viewport: (f32, f32)) -> DomLayout {
|
|||||||
}
|
}
|
||||||
css_rules.push((".subtext".into(), "color: #828282; font-size: 7pt".into()));
|
css_rules.push((".subtext".into(), "color: #828282; font-size: 7pt".into()));
|
||||||
css_rules.push((".subline".into(), "color: #828282; font-size: 7pt".into()));
|
css_rules.push((".subline".into(), "color: #828282; font-size: 7pt".into()));
|
||||||
|
css_rules.push((".hnname".into(), "margin-right: 10px; margin-left: 2px".into()));
|
||||||
|
css_rules.push((".pagetop".into(), "align-items: center".into()));
|
||||||
|
css_rules.push((".votearrow".into(), "width: 10px; height: 10px; margin-top: 2px".into()));
|
||||||
css_rules.push((".rank".into(), "color: #828282".into()));
|
css_rules.push((".rank".into(), "color: #828282".into()));
|
||||||
css_rules.push((".comhead".into(), "color: #828282; font-size: 7pt".into()));
|
css_rules.push((".comhead".into(), "color: #828282; font-size: 7pt".into()));
|
||||||
css_rules.push(("a".into(), "color: #000000".into()));
|
css_rules.push(("a".into(), "color: #000000".into()));
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ pub struct LayoutStyle {
|
|||||||
pub color: Option<[u8; 4]>,
|
pub color: Option<[u8; 4]>,
|
||||||
pub border_color: Option<[u8; 4]>,
|
pub border_color: Option<[u8; 4]>,
|
||||||
pub font_size: Option<f32>,
|
pub font_size: Option<f32>,
|
||||||
|
pub font_weight: Option<String>,
|
||||||
pub align_items: Option<taffy::AlignItems>,
|
pub align_items: Option<taffy::AlignItems>,
|
||||||
pub flex_direction: Option<taffy::FlexDirection>,
|
pub flex_direction: Option<taffy::FlexDirection>,
|
||||||
pub justify_content: Option<taffy::JustifyContent>,
|
pub justify_content: Option<taffy::JustifyContent>,
|
||||||
|
|||||||
@@ -97,18 +97,35 @@ pub fn paint_dom(tree: &DomTree, viewport: (f32, f32)) -> Option<Pixmap> {
|
|||||||
if name.local.as_ref() == "img" {
|
if name.local.as_ref() == "img" {
|
||||||
let src = node.get_attribute("src").unwrap_or("");
|
let src = node.get_attribute("src").unwrap_or("");
|
||||||
if src.contains("y18") {
|
if src.contains("y18") {
|
||||||
let paint = tiny_skia::Paint {
|
let mut paint = tiny_skia::Paint {
|
||||||
shader: tiny_skia::Shader::SolidColor(tiny_skia::Color::from_rgba8(255, 102, 0, 255)),
|
shader: tiny_skia::Shader::SolidColor(tiny_skia::Color::from_rgba8(255, 255, 255, 255)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
if let Some(r) = Rect::from_xywh(rect.x - 1.0, rect.y - 1.0, rect.width.max(18.0) + 2.0, rect.height.max(18.0) + 2.0) {
|
||||||
|
pixmap.fill_rect(r, &paint, Transform::identity(), None);
|
||||||
|
}
|
||||||
|
paint.shader = tiny_skia::Shader::SolidColor(tiny_skia::Color::from_rgba8(255, 102, 0, 255));
|
||||||
if let Some(r) = Rect::from_xywh(rect.x, rect.y, rect.width.max(18.0), rect.height.max(18.0)) {
|
if let Some(r) = Rect::from_xywh(rect.x, rect.y, rect.width.max(18.0), rect.height.max(18.0)) {
|
||||||
pixmap.fill_rect(r, &paint, Transform::identity(), None);
|
pixmap.fill_rect(r, &paint, Transform::identity(), None);
|
||||||
draw_text(&mut pixmap, "Y", rect.x + 4.0, rect.y + 2.0, [255, 255, 255, 255], 14.0);
|
draw_text(&mut pixmap, "Y", rect.x + 4.0, rect.y + 2.0, [255, 255, 255, 255], 14.0, false);
|
||||||
}
|
}
|
||||||
} else if let Some(src) = node.get_attribute("src") {
|
} else if let Some(src) = node.get_attribute("src") {
|
||||||
paint_image(src, &rect, &mut pixmap);
|
paint_image(src, &rect, &mut pixmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if node.get_attribute("class").unwrap_or_default() == "votearrow" {
|
||||||
|
let mut pb = tiny_skia::PathBuilder::new();
|
||||||
|
pb.move_to(rect.x + rect.width * 0.5, rect.y);
|
||||||
|
pb.line_to(rect.x + rect.width, rect.y + rect.height);
|
||||||
|
pb.line_to(rect.x, rect.y + rect.height);
|
||||||
|
pb.close();
|
||||||
|
if let Some(path) = pb.finish() {
|
||||||
|
let mut paint = tiny_skia::Paint::default();
|
||||||
|
paint.set_color_rgba8(130, 130, 130, 255);
|
||||||
|
pixmap.fill_path(&path, &paint, FillRule::Winding, Transform::identity(), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(pixmap)
|
Some(pixmap)
|
||||||
}
|
}
|
||||||
@@ -142,6 +159,8 @@ fn paint_text_node(
|
|||||||
let color = style.color.unwrap_or([0, 0, 0, 255]);
|
let color = style.color.unwrap_or([0, 0, 0, 255]);
|
||||||
let fsize = style.font_size.unwrap_or(16.0);
|
let fsize = style.font_size.unwrap_or(16.0);
|
||||||
|
|
||||||
|
let is_bold = style.font_weight.as_deref() == Some("bold");
|
||||||
|
|
||||||
// Get geometry of the text node itself!
|
// Get geometry of the text node itself!
|
||||||
let rect = match laid.rects.get(&nid) {
|
let rect = match laid.rects.get(&nid) {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
@@ -149,11 +168,11 @@ fn paint_text_node(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Paint text at rect.x, rect.y
|
// Paint text at rect.x, rect.y
|
||||||
draw_text(pixmap, text, rect.x, rect.y, color, fsize);
|
draw_text(pixmap, text, rect.x, rect.y, color, fsize, is_bold);
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_text(pixmap: &mut Pixmap, text: &str, x: f32, y: f32, color: [u8; 4], size: f32) {
|
fn draw_text(pixmap: &mut Pixmap, text: &str, x: f32, y: f32, color: [u8; 4], size: f32, is_bold: bool) {
|
||||||
let font = FontRef::try_from_slice(FONT_BYTES).unwrap();
|
let font = FontRef::try_from_slice(FONT_BYTES).unwrap();
|
||||||
let scale = PxScale::from(size);
|
let scale = PxScale::from(size);
|
||||||
let scaled_font = font.as_scaled(scale);
|
let scaled_font = font.as_scaled(scale);
|
||||||
@@ -179,25 +198,30 @@ fn draw_text(pixmap: &mut Pixmap, text: &str, x: f32, y: f32, color: [u8; 4], si
|
|||||||
if px >= 0 && px < width && py >= 0 && py < height {
|
if px >= 0 && px < width && py >= 0 && py < height {
|
||||||
let alpha = (a_full as f32 * c) as u8;
|
let alpha = (a_full as f32 * c) as u8;
|
||||||
if alpha > 0 {
|
if alpha > 0 {
|
||||||
let idx = (py * width + px) as usize;
|
let mut px_indices = vec![(py * width + px) as usize];
|
||||||
let dst = pixels[idx];
|
if is_bold && px + 1 < width {
|
||||||
|
px_indices.push((py * width + px + 1) as usize);
|
||||||
|
}
|
||||||
|
for idx in px_indices {
|
||||||
|
let dst = pixels[idx];
|
||||||
|
|
||||||
let src_a = alpha as u32;
|
let src_a = alpha as u32;
|
||||||
let src_r = (r as u32 * src_a) / 255;
|
let src_r = (r as u32 * src_a) / 255;
|
||||||
let src_g = (g as u32 * src_a) / 255;
|
let src_g = (g as u32 * src_a) / 255;
|
||||||
let src_b = (b as u32 * src_a) / 255;
|
let src_b = (b as u32 * src_a) / 255;
|
||||||
|
|
||||||
let dst_a = dst.alpha() as u32;
|
let dst_a = dst.alpha() as u32;
|
||||||
let out_a = src_a + (dst_a * (255 - src_a) / 255);
|
let out_a = src_a + (dst_a * (255 - src_a) / 255);
|
||||||
|
|
||||||
if out_a > 0 {
|
if out_a > 0 {
|
||||||
let out_r = src_r + (dst.red() as u32 * (255 - src_a) / 255);
|
let out_r = src_r + (dst.red() as u32 * (255 - src_a) / 255);
|
||||||
let out_g = src_g + (dst.green() as u32 * (255 - src_a) / 255);
|
let out_g = src_g + (dst.green() as u32 * (255 - src_a) / 255);
|
||||||
let out_b = src_b + (dst.blue() as u32 * (255 - src_a) / 255);
|
let out_b = src_b + (dst.blue() as u32 * (255 - src_a) / 255);
|
||||||
|
|
||||||
pixels[idx] = tiny_skia::PremultipliedColorU8::from_rgba(
|
pixels[idx] = tiny_skia::PremultipliedColorU8::from_rgba(
|
||||||
out_r as u8, out_g as u8, out_b as u8, out_a as u8
|
out_r as u8, out_g as u8, out_b as u8, out_a as u8
|
||||||
).unwrap_or_else(|| tiny_skia::PremultipliedColorU8::from_rgba(0,0,0,0).unwrap());
|
).unwrap_or_else(|| tiny_skia::PremultipliedColorU8::from_rgba(0,0,0,0).unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ pub fn compute_style(tag: &str, inline_css: Option<&str>) -> LayoutStyle {
|
|||||||
/// inline/text layout arrives with the text/paint phase.
|
/// inline/text layout arrives with the text/paint phase.
|
||||||
pub fn ua_style(tag: &str) -> LayoutStyle {
|
pub fn ua_style(tag: &str) -> LayoutStyle {
|
||||||
let mut style = LayoutStyle::default();
|
let mut style = LayoutStyle::default();
|
||||||
|
if tag == "b" || tag == "strong" {
|
||||||
|
style.font_weight = Some("bold".into());
|
||||||
|
}
|
||||||
|
|
||||||
style.display = match tag {
|
style.display = match tag {
|
||||||
"span" | "a" | "b" | "i" | "strong" | "em" | "font" => Display::Inline,
|
"span" | "a" | "b" | "i" | "strong" | "em" | "font" => Display::Inline,
|
||||||
"tr" => Display::Flex,
|
"tr" => Display::Flex,
|
||||||
@@ -88,7 +92,16 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) {
|
|||||||
"padding-right" => set_edge(&mut style.padding, Side::Right, px(value)),
|
"padding-right" => set_edge(&mut style.padding, Side::Right, px(value)),
|
||||||
"padding-bottom" => set_edge(&mut style.padding, Side::Bottom, px(value)),
|
"padding-bottom" => set_edge(&mut style.padding, Side::Bottom, px(value)),
|
||||||
"padding-left" => set_edge(&mut style.padding, Side::Left, px(value)),
|
"padding-left" => set_edge(&mut style.padding, Side::Left, px(value)),
|
||||||
"border-width" | "border" => { if let Some(e) = edges(value) { style.border = e; } }
|
"border" => {
|
||||||
|
for p in value.split_whitespace() {
|
||||||
|
if let Some(c) = parse_color(p) {
|
||||||
|
style.border_color = Some(c);
|
||||||
|
} else if p.ends_with("px") || p.chars().all(|c| c.is_ascii_digit()) {
|
||||||
|
if let Some(e) = edges(p) { style.border = e; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"border-width" => { if let Some(e) = edges(value) { style.border = e; } }
|
||||||
"border-top-width" | "border-top" => set_edge(&mut style.border, Side::Top, px(value)),
|
"border-top-width" | "border-top" => set_edge(&mut style.border, Side::Top, px(value)),
|
||||||
"border-right-width" | "border-right" => set_edge(&mut style.border, Side::Right, px(value)),
|
"border-right-width" | "border-right" => set_edge(&mut style.border, Side::Right, px(value)),
|
||||||
"border-bottom-width" | "border-bottom" => set_edge(&mut style.border, Side::Bottom, px(value)),
|
"border-bottom-width" | "border-bottom" => set_edge(&mut style.border, Side::Bottom, px(value)),
|
||||||
@@ -97,6 +110,7 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) {
|
|||||||
"color" => style.color = parse_color(value),
|
"color" => style.color = parse_color(value),
|
||||||
"border-color" => style.border_color = parse_color(value),
|
"border-color" => style.border_color = parse_color(value),
|
||||||
"font-size" => style.font_size = px(value),
|
"font-size" => style.font_size = px(value),
|
||||||
|
"font-weight" => style.font_weight = Some(value.to_string()),
|
||||||
"text-align" => {
|
"text-align" => {
|
||||||
if value == "right" {
|
if value == "right" {
|
||||||
style.justify_content = Some(taffy::JustifyContent::FlexEnd);
|
style.justify_content = Some(taffy::JustifyContent::FlexEnd);
|
||||||
@@ -104,6 +118,15 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) {
|
|||||||
style.justify_content = Some(taffy::JustifyContent::Center);
|
style.justify_content = Some(taffy::JustifyContent::Center);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"align-items" => {
|
||||||
|
if value == "center" {
|
||||||
|
style.align_items = Some(taffy::AlignItems::Center);
|
||||||
|
} else if value == "flex-start" || value == "start" {
|
||||||
|
style.align_items = Some(taffy::AlignItems::FlexStart);
|
||||||
|
} else if value == "flex-end" || value == "end" {
|
||||||
|
style.align_items = Some(taffy::AlignItems::FlexEnd);
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user