diff --git a/src/lib.rs b/src/lib.rs index d0854b7e..7c7c677c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -423,6 +423,160 @@ mod tests { "# }); + test(r#" + .foo { + border: 1px solid black; + border-width: 1px 1px 0 0; + } + "#, indoc! {r#" + .foo { + border: 1px solid #000; + border-width: 1px 1px 0 0; + } + "#}); + + test(r#" + .foo { + border-top: 1px solid black; + border-bottom: 1px solid black; + border-left: 2px solid black; + border-right: 2px solid black; + } + "#, indoc! {r#" + .foo { + border: 1px solid #000; + border-width: 1px 2px; + } + "#}); + + test(r#" + .foo { + border-top: 1px solid black; + border-bottom: 1px solid black; + border-left: 2px solid black; + border-right: 1px solid black; + } + "#, indoc! {r#" + .foo { + border: 1px solid #000; + border-left-width: 2px; + } + "#}); + + test(r#" + .foo { + border-top: 1px solid black; + border-bottom: 1px solid black; + border-left: 1px solid red; + border-right: 1px solid red; + } + "#, indoc! {r#" + .foo { + border: 1px solid #000; + border-color: #000 red; + } + "#}); + + test(r#" + .foo { + border-block-start: 1px solid black; + border-block-end: 1px solid black; + border-inline-start: 1px solid red; + border-inline-end: 1px solid red; + } + "#, indoc! {r#" + .foo { + border: 1px solid #000; + border-inline-color: red; + } + "#}); + + test(r#" + .foo { + border-block-start: 1px solid black; + border-block-end: 1px solid black; + border-inline-start: 2px solid black; + border-inline-end: 2px solid black; + } + "#, indoc! {r#" + .foo { + border: 1px solid #000; + border-inline-width: 2px; + } + "#}); + + test(r#" + .foo { + border-block-start: 1px solid black; + border-block-end: 1px solid black; + border-inline-start: 2px solid red; + border-inline-end: 2px solid red; + } + "#, indoc! {r#" + .foo { + border: 1px solid #000; + border-inline: 2px solid red; + } + "#}); + + test(r#" + .foo { + border-block-start: 1px solid black; + border-block-end: 1px solid black; + border-inline-start: 2px solid red; + border-inline-end: 3px solid red; + } + "#, indoc! {r#" + .foo { + border: 1px solid #000; + border-inline-start: 2px solid red; + border-inline-end: 3px solid red; + } + "#}); + + test(r#" + .foo { + border-block-start: 2px solid black; + border-block-end: 1px solid black; + border-inline-start: 2px solid red; + border-inline-end: 2px solid red; + } + "#, indoc! {r#" + .foo { + border: 2px solid red; + border-block-start-color: #000; + border-block-end: 1px solid #000; + } + "#}); + + test(r#" + .foo { + border-block-start: 2px solid red; + border-block-end: 1px solid red; + border-inline-start: 2px solid red; + border-inline-end: 2px solid red; + } + "#, indoc! {r#" + .foo { + border: 2px solid red; + border-block-end-width: 1px; + } + "#}); + + test(r#" + .foo { + border-block-start: 2px solid red; + border-block-end: 2px solid red; + border-inline-start: 2px solid red; + border-inline-end: 1px solid red; + } + "#, indoc! {r#" + .foo { + border: 2px solid red; + border-inline-end-width: 1px; + } + "#}); + prefix_test(r#" .foo { border-block: 2px solid red; diff --git a/src/properties/border.rs b/src/properties/border.rs index d5e50282..23c73287 100644 --- a/src/properties/border.rs +++ b/src/properties/border.rs @@ -600,6 +600,34 @@ impl<'i> BorderHandler<'i> { $inline_end: expr, $is_logical: expr ) => { + macro_rules! shorthand { + ($prop: ident, $key: ident) => {{ + let has_prop = $block_start.$key.is_some() && $block_end.$key.is_some() && $inline_start.$key.is_some() && $inline_end.$key.is_some(); + if has_prop { + if !$is_logical || ($block_start.$key == $block_end.$key && $block_end.$key == $inline_start.$key && $inline_start.$key == $inline_end.$key) { + let rect = Rect::new( + std::mem::take(&mut $block_start.$key).unwrap(), + std::mem::take(&mut $inline_end.$key).unwrap(), + std::mem::take(&mut $block_end.$key).unwrap(), + std::mem::take(&mut $inline_start.$key).unwrap() + ); + prop!($prop => rect); + } + } + }}; + } + + macro_rules! logical_shorthand { + ($prop: ident, $key: ident, $start: expr, $end: expr) => {{ + let has_prop = $start.$key.is_some() && $start.$key == $end.$key; + if has_prop { + prop!($prop => std::mem::take(&mut $start.$key).unwrap()); + $end.$key = None; + } + has_prop + }}; + } + if $block_start.is_valid() && $block_end.is_valid() && $inline_start.is_valid() && $inline_end.is_valid() { let top_eq_bottom = $block_start == $block_end; let left_eq_right = $inline_start == $inline_end; @@ -608,6 +636,34 @@ impl<'i> BorderHandler<'i> { let bottom_eq_left = $block_end == $inline_start; let bottom_eq_right = $block_end == $inline_end; + macro_rules! is_eq { + ($key: ident) => { + $block_start.$key == $block_end.$key && + $inline_start.$key == $inline_end.$key && + $inline_start.$key == $block_start.$key + }; + } + + macro_rules! prop_diff { + ($border: expr, $fallback: expr, $border_fallback: literal) => { + if !$is_logical && is_eq!(color) && is_eq!(style) { + dest.push(Property::Border($border.to_border())); + shorthand!(BorderWidth, width); + } else if !$is_logical && is_eq!(width) && is_eq!(style) { + dest.push(Property::Border($border.to_border())); + shorthand!(BorderColor, color); + } else if !$is_logical && is_eq!(width) && is_eq!(color) { + dest.push(Property::Border($border.to_border())); + shorthand!(BorderStyle, style); + } else { + if $border_fallback { + dest.push(Property::Border($border.to_border())); + } + $fallback + } + }; + } + macro_rules! side_diff { ($border: expr, $other: expr, $prop: ident, $width: ident, $style: ident, $color: ident) => { let eq_width = $border.width == $other.width; @@ -643,52 +699,63 @@ impl<'i> BorderHandler<'i> { dest.push(Property::Border($inline_start.to_border())); side_diff!($inline_start, $block_end, $block_end_prop, $block_end_width, $block_end_style, $block_end_color); } else if top_eq_bottom { - dest.push(Property::Border($block_start.to_border())); - side_diff!($block_start, $inline_start, $inline_start_prop, $inline_start_width, $inline_start_style, $inline_start_color); - side_diff!($block_start, $inline_end, $inline_end_prop, $inline_end_width, $inline_end_style, $inline_end_color); - } else if left_eq_right { - dest.push(Property::Border($inline_start.to_border())); - side_diff!($inline_start, $block_start, $block_start_prop, $block_start_width, $block_start_style, $block_start_color); - side_diff!($inline_start, $block_end, $block_end_prop, $block_end_width, $block_end_style, $block_end_color); - } else if bottom_eq_right { - dest.push(Property::Border($block_end.to_border())); - side_diff!($block_end, $block_start, $block_start_prop, $block_start_width, $block_start_style, $block_start_color); - side_diff!($block_end, $inline_start, $inline_start_prop, $inline_start_width, $inline_start_style, $inline_start_color); - } else { - prop!($block_start_prop => $block_start.to_border()); - prop!($block_end_prop => $block_end.to_border()); - prop!($inline_start_prop => $inline_start.to_border()); - prop!($inline_end_prop => $inline_end.to_border()); - } - } else { - macro_rules! shorthand { - ($prop: ident, $key: ident) => {{ - let has_prop = $block_start.$key.is_some() && $block_end.$key.is_some() && $inline_start.$key.is_some() && $inline_end.$key.is_some(); - if has_prop { - if !$is_logical || ($block_start.$key == $block_end.$key && $block_end.$key == $inline_start.$key && $inline_start.$key == $inline_end.$key) { - let rect = Rect::new( - std::mem::take(&mut $block_start.$key).unwrap(), - std::mem::take(&mut $inline_end.$key).unwrap(), - std::mem::take(&mut $block_end.$key).unwrap(), - std::mem::take(&mut $inline_start.$key).unwrap() - ); - prop!($prop => rect); + prop_diff!($block_start, { + // Try to use border-inline shorthands for the opposide direction if possible. + let mut handled = false; + if $is_logical { + let mut diff = 0; + if $inline_start.width != $block_start.width || $inline_end.width != $block_start.width { + diff += 1; + } + if $inline_start.style != $block_start.style || $inline_end.style != $block_start.style { + diff += 1; + } + if $inline_start.color != $block_start.color || $inline_end.color != $block_start.color { + diff += 1; + } + + if diff == 1 { + if $inline_start.width != $block_start.width && $inline_start.width == $inline_end.width { + prop!(BorderInlineWidth => $inline_start.width.clone().unwrap()); + handled = true; + } else if $inline_start.style != $block_start.style && $inline_start.style == $inline_end.style { + prop!(BorderInlineStyle => $inline_start.style.clone().unwrap()); + handled = true; + } else if $inline_start.color != $block_start.color && $inline_start.color == $inline_end.color { + prop!(BorderInlineColor => $inline_start.color.clone().unwrap()); + handled = true; + } + } else if diff > 1 && $inline_start.width == $inline_end.width && $inline_start.style == $inline_end.style && $inline_start.color == $inline_end.color { + prop!(BorderInline => $inline_start.to_border()); + handled = true; } } - }}; - } - macro_rules! logical_shorthand { - ($prop: ident, $key: ident, $start: expr, $end: expr) => {{ - let has_prop = $start.$key.is_some() && $start.$key == $end.$key; - if has_prop { - prop!($prop => std::mem::take(&mut $start.$key).unwrap()); - $end.$key = None; + if !handled { + side_diff!($block_start, $inline_start, $inline_start_prop, $inline_start_width, $inline_start_style, $inline_start_color); + side_diff!($block_start, $inline_end, $inline_end_prop, $inline_end_width, $inline_end_style, $inline_end_color); } - has_prop - }}; + }, true); + } else if left_eq_right { + prop_diff!($inline_start, { + // We know already that top != bottom, so no need to try to use border-block. + side_diff!($inline_start, $block_start, $block_start_prop, $block_start_width, $block_start_style, $block_start_color); + side_diff!($inline_start, $block_end, $block_end_prop, $block_end_width, $block_end_style, $block_end_color); + }, true); + } else if bottom_eq_right { + prop_diff!($block_end, { + side_diff!($block_end, $block_start, $block_start_prop, $block_start_width, $block_start_style, $block_start_color); + side_diff!($block_end, $inline_start, $inline_start_prop, $inline_start_width, $inline_start_style, $inline_start_color); + }, true); + } else { + prop_diff!($block_start, { + prop!($block_start_prop => $block_start.to_border()); + prop!($block_end_prop => $block_end.to_border()); + prop!($inline_start_prop => $inline_start.to_border()); + prop!($inline_end_prop => $inline_end.to_border()); + }, false); } - + } else { shorthand!(BorderStyle, style); shorthand!(BorderWidth, width); shorthand!(BorderColor, color);