Changing stylesheet to accept multiple selectors, and adding in pseudo macro for classes

This commit is contained in:
Pauan 2019-08-05 03:39:40 +02:00
parent 2bed86b49e
commit 8a056e9eec
4 changed files with 116 additions and 40 deletions

View File

@ -89,13 +89,13 @@ pub(crate) fn create_stylesheet() -> CssStyleSheet {
})
}
pub(crate) fn make_style_rule(sheet: &CssStyleSheet, selector: &str) -> CssStyleRule {
pub(crate) fn make_style_rule(sheet: &CssStyleSheet, selector: &str) -> Result<CssStyleRule, JsValue> {
let rules = sheet.css_rules().unwrap_throw();
let length = rules.length();
// TODO don't return u32 ?
sheet.insert_rule_with_index(&format!("{}{{}}", selector), length).unwrap_throw();
sheet.insert_rule_with_index(&format!("{}{{}}", selector), length)?;
// TODO use dyn_into ?
rules.get(length).unwrap_throw().unchecked_into()
Ok(rules.get(length).unwrap_throw().unchecked_into())
}

View File

@ -3,7 +3,6 @@ use std::convert::AsRef;
use std::marker::PhantomData;
use std::future::Future;
use std::task::{Context, Poll};
use std::sync::atomic::{AtomicU32, Ordering};
use lazy_static::lazy_static;
use futures_signals::signal::{Signal, not};
@ -301,7 +300,7 @@ fn set_style<A, B>(style: &CssStyleDeclaration, name: &A, value: B, important: b
let mut names = vec![];
let mut values = vec![];
fn try_set_style(style: &CssStyleDeclaration, names: &mut Vec<String>, values: &mut Vec<String>, name: &str, value: &str, important: bool) -> bool {
fn try_set_style(style: &CssStyleDeclaration, names: &mut Vec<String>, values: &mut Vec<String>, name: &str, value: &str, important: bool) -> Option<()> {
assert!(value != "");
// TODO handle browser prefixes ?
@ -313,25 +312,25 @@ fn set_style<A, B>(style: &CssStyleDeclaration, name: &A, value: B, important: b
let is_changed = bindings::get_style(style, name) != "";
if is_changed {
true
Some(())
} else {
names.push(String::from(name));
values.push(String::from(value));
false
None
}
}
let okay = name.any(|name| {
let okay = name.find_map(|name| {
let name: &str = intern(name);
value.any(|value| {
value.find_map(|value| {
// TODO should this intern ?
try_set_style(style, &mut names, &mut values, &name, &value, important)
})
});
if !okay {
if let None = okay {
// TODO maybe make this configurable
panic!("style is incorrect:\n names: {}\n values: {}", names.join(", "), values.join(", "));
}
@ -917,17 +916,41 @@ pub struct StylesheetBuilder {
// TODO remove the CssStyleRule when this is discarded
impl StylesheetBuilder {
// TODO should this inline ?
#[doc(hidden)]
#[inline]
pub fn new(selector: &str) -> Self {
pub fn __internal_new<A>(selector: A) -> Self where A: MultiStr {
// TODO can this be made faster ?
// TODO somehow share this safely between threads ?
thread_local! {
static STYLESHEET: CssStyleSheet = bindings::create_stylesheet();
}
let element = STYLESHEET.with(move |stylesheet| {
fn try_make(stylesheet: &CssStyleSheet, selector: &str, selectors: &mut Vec<String>) -> Option<CssStyleDeclaration> {
// TODO maybe intern the selector ?
bindings::make_style_rule(stylesheet, &selector).style()
if let Ok(declaration) = bindings::make_style_rule(stylesheet, selector) {
Some(declaration.style())
} else {
selectors.push(String::from(selector));
None
}
}
let element = STYLESHEET.with(move |stylesheet| {
let mut selectors = vec![];
let okay = selector.find_map(|selector| {
try_make(stylesheet, selector, &mut selectors)
});
if let Some(okay) = okay {
okay
} else {
// TODO maybe make this configurable
panic!("selectors are incorrect:\n {}", selectors.join("\n "));
}
});
Self {
@ -976,7 +999,8 @@ impl StylesheetBuilder {
// TODO return a Handle
#[inline]
pub fn done(mut self) {
#[doc(hidden)]
pub fn __internal_done(mut self) {
self.callbacks.trigger_after_insert();
// This prevents it from triggering after_remove
@ -993,27 +1017,24 @@ pub struct ClassBuilder {
}
impl ClassBuilder {
#[doc(hidden)]
#[inline]
pub fn new() -> Self {
let class_name = {
// TODO replace this with a global counter in JavaScript ?
// TODO can this be made more efficient ?
static CLASS_ID: AtomicU32 = AtomicU32::new(0);
// TODO check for overflow ?
let id = CLASS_ID.fetch_add(1, Ordering::Relaxed);
// TODO make this more efficient ?
format!("__class_{}__", id)
};
pub fn __internal_new() -> Self {
let class_name = __internal::make_class_id();
Self {
// TODO make this more efficient ?
stylesheet: StylesheetBuilder::new(&format!(".{}", class_name)),
stylesheet: StylesheetBuilder::__internal_new(&format!(".{}", class_name)),
class_name,
}
}
#[doc(hidden)]
#[inline]
pub fn __internal_class_name(&self) -> &str {
&self.class_name
}
#[inline]
pub fn style<B, C>(mut self, name: B, value: C) -> Self
where B: MultiStr,
@ -1053,14 +1074,57 @@ impl ClassBuilder {
}
// TODO return a Handle ?
#[doc(hidden)]
#[inline]
pub fn done(self) -> String {
self.stylesheet.done();
pub fn __internal_done(self) -> String {
self.stylesheet.__internal_done();
self.class_name
}
}
#[doc(hidden)]
pub mod __internal {
use std::sync::atomic::{AtomicU32, Ordering};
use crate::traits::MultiStr;
pub fn make_class_id() -> String {
// TODO replace this with a global counter in JavaScript ?
// TODO can this be made more efficient ?
static CLASS_ID: AtomicU32 = AtomicU32::new(0);
// TODO check for overflow ?
// TODO should this be SeqCst ?
let id = CLASS_ID.fetch_add(1, Ordering::Relaxed);
// TODO make this more efficient ?
format!("__class_{}__", id)
}
pub struct Pseudo<'a, A> {
class_name: &'a str,
pseudos: A,
}
impl<'a, A> Pseudo<'a, A> where A: MultiStr {
#[inline]
pub fn new(class_name: &'a str, pseudos: A) -> Self {
Self { class_name, pseudos }
}
}
impl<'a, A> MultiStr for Pseudo<'a, A> where A: MultiStr {
#[inline]
fn find_map<B, F>(&self, mut f: F) -> Option<B> where F: FnMut(&str) -> Option<B> {
self.pseudos.find_map(|x| {
f(&format!(".{}{}", self.class_name, x))
})
}
}
}
#[cfg(test)]
mod tests {

View File

@ -66,16 +66,28 @@ macro_rules! stylesheet {
$crate::stylesheet!($rule, {})
};
($rule:expr, { $($methods:tt)* }) => {
$crate::apply_methods!($crate::StylesheetBuilder::new($rule), { $($methods)* }).done()
$crate::StylesheetBuilder::__internal_done($crate::apply_methods!($crate::StylesheetBuilder::__internal_new($rule), { $($methods)* }))
};
}
#[macro_export]
macro_rules! class {
($($methods:tt)*) => {
$crate::apply_methods!($crate::ClassBuilder::new(), { $($methods)* }).done()
($($methods:tt)*) => {{
$crate::ClassBuilder::__internal_done($crate::apply_methods!($crate::ClassBuilder::__internal_new(), { $($methods)* }))
}};
}
#[macro_export]
macro_rules! pseudo {
($this:ident, $rules:expr) => {
$crate::pseudo!($this, $rules, {})
};
($this:ident, $rules:expr, { $($methods:tt)* }) => {{
$crate::stylesheet!($crate::__internal::Pseudo::new($crate::ClassBuilder::__internal_class_name(&$this), $rules), { $($methods)* });
$this
}};
}

View File

@ -67,20 +67,20 @@ impl<A, C> AsStr for RefFn<A, str, C> where C: Fn(&A) -> &str {
pub trait MultiStr {
fn any<F>(&self, f: F) -> bool where F: FnMut(&str) -> bool;
fn find_map<A, F>(&self, f: F) -> Option<A> where F: FnMut(&str) -> Option<A>;
#[inline]
fn each<F>(&self, mut f: F) where F: FnMut(&str) {
self.any(|x| {
let _: Option<()> = self.find_map(|x| {
f(x);
false
None
});
}
}
impl<A> MultiStr for A where A: AsStr {
#[inline]
fn any<F>(&self, mut f: F) -> bool where F: FnMut(&str) -> bool {
fn find_map<B, F>(&self, mut f: F) -> Option<B> where F: FnMut(&str) -> Option<B> {
f(self.as_str())
}
}
@ -96,8 +96,8 @@ impl<A> MultiStr for A where A: AsStr {
// TODO it would be great to use IntoIterator or Iterator instead
impl<'a, A, C> MultiStr for RefFn<A, [&'a str], C> where C: Fn(&A) -> &[&'a str] {
#[inline]
fn any<F>(&self, mut f: F) -> bool where F: FnMut(&str) -> bool {
self.call_ref().iter().any(|x| f(x))
fn find_map<B, F>(&self, mut f: F) -> Option<B> where F: FnMut(&str) -> Option<B> {
self.call_ref().iter().find_map(|x| f(x))
}
}
@ -105,8 +105,8 @@ macro_rules! array_multi_str {
($size:expr) => {
impl<A> MultiStr for [A; $size] where A: AsStr {
#[inline]
fn any<F>(&self, mut f: F) -> bool where F: FnMut(&str) -> bool {
self.iter().any(|x| f(x.as_str()))
fn find_map<B, F>(&self, mut f: F) -> Option<B> where F: FnMut(&str) -> Option<B> {
self.iter().find_map(|x| f(x.as_str()))
}
}
};