<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::AttrBinding;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::Node;
use dom::window::Window;
use dom::virtualmethods::vtable_for;
use servo_util::atom::Atom;
use servo_util::namespace;
use servo_util::namespace::Namespace;
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
use std::cell::{Ref, RefCell};
use std::mem;

pub enum AttrSettingType {
    FirstSetAttr,
    ReplacedAttr,
}

#[deriving(PartialEq, Clone, Encodable)]
pub enum AttrValue {
    StringAttrValue(DOMString),
    TokenListAttrValue(DOMString, Vec<(uint, uint)>),
    UIntAttrValue(DOMString, u32),
    AtomAttrValue(Atom),
}

impl AttrValue {
    pub fn from_tokenlist(list: DOMString) -> AttrValue {
        let mut indexes = vec![];
        let mut last_index: uint = 0;
        for (index, ch) in list.as_slice().char_indices() {
            if HTML_SPACE_CHARACTERS.iter().any(|&space| space == ch) {
                indexes.push((last_index, index));
                last_index = index + 1;
            }
        }
        return TokenListAttrValue(list, indexes);
    }

    pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
        let result: u32 = from_str(string.as_slice()).unwrap_or(default);
        UIntAttrValue(string, result)
    }

    pub fn from_atomic(string: DOMString) -> AttrValue {
        let value = Atom::from_slice(string.as_slice());
        AtomAttrValue(value)
    }

    pub fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            StringAttrValue(ref value) |
            TokenListAttrValue(ref value, _) |
            UIntAttrValue(ref value, _) => value.as_slice(),
            AtomAttrValue(ref value) => value.as_slice(),
        }
    }
}

#[deriving(Encodable)]
pub struct Attr {
    reflector_: Reflector,
    local_name: Atom,
    value: Traceable<RefCell<AttrValue>>,
    pub name: Atom,
    pub namespace: Namespace,
    pub prefix: Option<DOMString>,

    /// the element that owns this attribute.
    owner: JS<Element>,
}

impl Reflectable for Attr {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl Attr {
    fn new_inherited(local_name: Atom, value: AttrValue,
                     name: Atom, namespace: Namespace,
                     prefix: Option<DOMString>, owner: &JSRef<Element>) -> Attr {
        Attr {
            reflector_: Reflector::new(),
            local_name: local_name,
            value: Traceable::new(RefCell::new(value)),
            name: name,
            namespace: namespace,
            prefix: prefix,
            owner: JS::from_rooted(owner),
        }
    }

    pub fn new(window: &JSRef<Window>, local_name: Atom, value: AttrValue,
               name: Atom, namespace: Namespace,
               prefix: Option<DOMString>, owner: &JSRef<Element>) -> Temporary<Attr> {
        let attr = Attr::new_inherited(local_name, value, name, namespace, prefix, owner);
        reflect_dom_object(box attr, &Window(*window), AttrBinding::Wrap)
    }

    pub fn set_value(&self, set_type: AttrSettingType, value: AttrValue) {
        let owner = self.owner.root();
        let node: &JSRef<Node> = NodeCast::from_ref(&*owner);
        let namespace_is_null = self.namespace == namespace::Null;

        match set_type {
            ReplacedAttr => {
                if namespace_is_null {
                    vtable_for(node).before_remove_attr(
                        self.local_name(),
                        self.value.deref().borrow().as_slice().to_string());
                }
            }
            FirstSetAttr => {}
        }

        *self.value.deref().borrow_mut() = value;

        if namespace_is_null {
            vtable_for(node).after_set_attr(
                self.local_name(),
                self.value.deref().borrow().as_slice().to_string());
        }
    }

    pub fn value<'a>(&'a self) -> Ref<'a, AttrValue> {
        self.value.deref().borrow()
    }

    pub fn local_name<'a>(&'a self) -> &'a Atom {
        &self.local_name
    }
}

impl<'a> AttrMethods for JSRef<'a, Attr> {
    fn LocalName(&self) -> DOMString {
        self.local_name().as_slice().to_string()
    }

    fn Value(&self) -> DOMString {
        self.value.deref().borrow().as_slice().to_string()
    }

    fn SetValue(&self, value: DOMString) {
        let owner = self.owner.root();
        let value = owner.deref().parse_attribute(
            &self.namespace, self.local_name(), value);
        self.set_value(ReplacedAttr, value);
    }

    fn Name(&self) -> DOMString {
        self.name.as_slice().to_string()
    }

    fn GetNamespaceURI(&self) -> Option<DOMString> {
        match self.namespace.to_str() {
            "" => None,
            url => Some(url.to_string()),
        }
    }

    fn GetPrefix(&self) -> Option<DOMString> {
        self.prefix.clone()
    }
}

pub trait AttrHelpersForLayout {
    unsafe fn value_ref_forever(&self) -> &'static str;
    unsafe fn value_atom_forever(&self) -> Option<Atom>;
}

impl AttrHelpersForLayout for Attr {
    unsafe fn value_ref_forever(&self) -> &'static str {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        value.as_slice()
    }

    unsafe fn value_atom_forever(&self) -> Option<Atom> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        match *value {
            AtomAttrValue(ref val) => Some(val.clone()),
            _ => None,
        }
    }
}
<commit_msg>Use Attr::value() whenever possible<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::AttrBinding;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::Node;
use dom::window::Window;
use dom::virtualmethods::vtable_for;
use servo_util::atom::Atom;
use servo_util::namespace;
use servo_util::namespace::Namespace;
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
use std::cell::{Ref, RefCell};
use std::mem;

pub enum AttrSettingType {
    FirstSetAttr,
    ReplacedAttr,
}

#[deriving(PartialEq, Clone, Encodable)]
pub enum AttrValue {
    StringAttrValue(DOMString),
    TokenListAttrValue(DOMString, Vec<(uint, uint)>),
    UIntAttrValue(DOMString, u32),
    AtomAttrValue(Atom),
}

impl AttrValue {
    pub fn from_tokenlist(list: DOMString) -> AttrValue {
        let mut indexes = vec![];
        let mut last_index: uint = 0;
        for (index, ch) in list.as_slice().char_indices() {
            if HTML_SPACE_CHARACTERS.iter().any(|&space| space == ch) {
                indexes.push((last_index, index));
                last_index = index + 1;
            }
        }
        return TokenListAttrValue(list, indexes);
    }

    pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
        let result: u32 = from_str(string.as_slice()).unwrap_or(default);
        UIntAttrValue(string, result)
    }

    pub fn from_atomic(string: DOMString) -> AttrValue {
        let value = Atom::from_slice(string.as_slice());
        AtomAttrValue(value)
    }

    pub fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            StringAttrValue(ref value) |
            TokenListAttrValue(ref value, _) |
            UIntAttrValue(ref value, _) => value.as_slice(),
            AtomAttrValue(ref value) => value.as_slice(),
        }
    }
}

#[deriving(Encodable)]
pub struct Attr {
    reflector_: Reflector,
    local_name: Atom,
    value: Traceable<RefCell<AttrValue>>,
    pub name: Atom,
    pub namespace: Namespace,
    pub prefix: Option<DOMString>,

    /// the element that owns this attribute.
    owner: JS<Element>,
}

impl Reflectable for Attr {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl Attr {
    fn new_inherited(local_name: Atom, value: AttrValue,
                     name: Atom, namespace: Namespace,
                     prefix: Option<DOMString>, owner: &JSRef<Element>) -> Attr {
        Attr {
            reflector_: Reflector::new(),
            local_name: local_name,
            value: Traceable::new(RefCell::new(value)),
            name: name,
            namespace: namespace,
            prefix: prefix,
            owner: JS::from_rooted(owner),
        }
    }

    pub fn new(window: &JSRef<Window>, local_name: Atom, value: AttrValue,
               name: Atom, namespace: Namespace,
               prefix: Option<DOMString>, owner: &JSRef<Element>) -> Temporary<Attr> {
        let attr = Attr::new_inherited(local_name, value, name, namespace, prefix, owner);
        reflect_dom_object(box attr, &Window(*window), AttrBinding::Wrap)
    }

    pub fn set_value(&self, set_type: AttrSettingType, value: AttrValue) {
        let owner = self.owner.root();
        let node: &JSRef<Node> = NodeCast::from_ref(&*owner);
        let namespace_is_null = self.namespace == namespace::Null;

        match set_type {
            ReplacedAttr => {
                if namespace_is_null {
                    vtable_for(node).before_remove_attr(
                        self.local_name(),
                        self.value().as_slice().to_string())
                }
            }
            FirstSetAttr => {}
        }

        *self.value.deref().borrow_mut() = value;

        if namespace_is_null {
            vtable_for(node).after_set_attr(
                self.local_name(),
                self.value().as_slice().to_string())
        }
    }

    pub fn value<'a>(&'a self) -> Ref<'a, AttrValue> {
        self.value.deref().borrow()
    }

    pub fn local_name<'a>(&'a self) -> &'a Atom {
        &self.local_name
    }
}

impl<'a> AttrMethods for JSRef<'a, Attr> {
    fn LocalName(&self) -> DOMString {
        self.local_name().as_slice().to_string()
    }

    fn Value(&self) -> DOMString {
        self.value().as_slice().to_string()
    }

    fn SetValue(&self, value: DOMString) {
        let owner = self.owner.root();
        let value = owner.deref().parse_attribute(
            &self.namespace, self.local_name(), value);
        self.set_value(ReplacedAttr, value);
    }

    fn Name(&self) -> DOMString {
        self.name.as_slice().to_string()
    }

    fn GetNamespaceURI(&self) -> Option<DOMString> {
        match self.namespace.to_str() {
            "" => None,
            url => Some(url.to_string()),
        }
    }

    fn GetPrefix(&self) -> Option<DOMString> {
        self.prefix.clone()
    }
}

pub trait AttrHelpersForLayout {
    unsafe fn value_ref_forever(&self) -> &'static str;
    unsafe fn value_atom_forever(&self) -> Option<Atom>;
}

impl AttrHelpersForLayout for Attr {
    unsafe fn value_ref_forever(&self) -> &'static str {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        value.as_slice()
    }

    unsafe fn value_atom_forever(&self) -> Option<Atom> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        match *value {
            AtomAttrValue(ref val) => Some(val.clone()),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>//! Endpoints that expose metadata about a crate
//!
//! These endpoints provide data that could be obtained directly from the
//! index or cached metadata which was extracted (client side) from the
//! `Cargo.toml` file.

use std::cmp::Reverse;

use crate::controllers::frontend_prelude::*;
use crate::controllers::helpers::pagination::PaginationOptions;

use crate::models::{
    Category, Crate, CrateCategory, CrateKeyword, CrateVersions, Keyword, RecentCrateDownloads,
    TopVersions, User, Version, VersionOwnerAction,
};
use crate::schema::*;
use crate::views::{
    EncodableCategory, EncodableCrate, EncodableDependency, EncodableKeyword, EncodableVersion,
};

use crate::models::krate::ALL_COLUMNS;

/// Handles the `GET /summary` route.
pub fn summary(req: &mut dyn RequestExt) -> EndpointResult {
    use crate::schema::crates::dsl::*;

    let conn = req.db_read_only()?;
    let num_crates = crates.count().get_result(&*conn)?;
    let num_downloads = metadata::table
        .select(metadata::total_downloads)
        .get_result(&*conn)?;

    let encode_crates = |data: Vec<(Crate, Option<i64>)>| -> AppResult<Vec<_>> {
        let recent_downloads = data.iter().map(|&(_, s)| s).collect::<Vec<_>>();

        let krates = data.into_iter().map(|(c, _)| c).collect::<Vec<_>>();

        let versions: Vec<Version> = krates.versions().load(&*conn)?;
        versions
            .grouped_by(&krates)
            .into_iter()
            .map(TopVersions::from_versions)
            .zip(krates)
            .zip(recent_downloads)
            .map(|((top_versions, krate), recent_downloads)| {
                Ok(EncodableCrate::from_minimal(
                    krate,
                    &top_versions,
                    None,
                    false,
                    recent_downloads,
                ))
            })
            .collect()
    };

    let selection = (ALL_COLUMNS, recent_crate_downloads::downloads.nullable());

    let new_crates = crates
        .left_join(recent_crate_downloads::table)
        .order(created_at.desc())
        .select(selection)
        .limit(10)
        .load(&*conn)?;
    let just_updated = crates
        .left_join(recent_crate_downloads::table)
        .filter(updated_at.ne(created_at))
        .order(updated_at.desc())
        .select(selection)
        .limit(10)
        .load(&*conn)?;
    let most_downloaded = crates
        .left_join(recent_crate_downloads::table)
        .then_order_by(downloads.desc())
        .select(selection)
        .limit(10)
        .load(&*conn)?;

    let most_recently_downloaded = crates
        .inner_join(recent_crate_downloads::table)
        .then_order_by(recent_crate_downloads::downloads.desc())
        .select(selection)
        .limit(10)
        .load(&*conn)?;

    let popular_keywords = keywords::table
        .order(keywords::crates_cnt.desc())
        .limit(10)
        .load(&*conn)?
        .into_iter()
        .map(Keyword::into)
        .collect();

    let popular_categories = Category::toplevel(&conn, "crates", 10, 0)?
        .into_iter()
        .map(Category::into)
        .collect();

    #[derive(Serialize)]
    struct R {
        num_downloads: i64,
        num_crates: i64,
        new_crates: Vec<EncodableCrate>,
        most_downloaded: Vec<EncodableCrate>,
        most_recently_downloaded: Vec<EncodableCrate>,
        just_updated: Vec<EncodableCrate>,
        popular_keywords: Vec<EncodableKeyword>,
        popular_categories: Vec<EncodableCategory>,
    }
    Ok(req.json(&R {
        num_downloads,
        num_crates,
        new_crates: encode_crates(new_crates)?,
        most_downloaded: encode_crates(most_downloaded)?,
        most_recently_downloaded: encode_crates(most_recently_downloaded)?,
        just_updated: encode_crates(just_updated)?,
        popular_keywords,
        popular_categories,
    }))
}

/// Handles the `GET /crates/:crate_id` route.
pub fn show(req: &mut dyn RequestExt) -> EndpointResult {
    let name = &req.params()["crate_id"];
    let conn = req.db_read_only()?;
    let krate: Crate = Crate::by_name(name).first(&*conn)?;

    let mut versions_and_publishers: Vec<(Version, Option<User>)> = krate
        .all_versions()
        .left_outer_join(users::table)
        .select((versions::all_columns, users::all_columns.nullable()))
        .load(&*conn)?;

    versions_and_publishers
        .sort_by_cached_key(|(version, _)| Reverse(semver::Version::parse(&version.num).ok()));

    let versions = versions_and_publishers
        .iter()
        .map(|(v, _)| v)
        .cloned()
        .collect::<Vec<_>>();
    let versions_publishers_and_audit_actions = versions_and_publishers
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((v, pb), aas)| (v, pb, aas))
        .collect::<Vec<_>>();
    let ids = versions_publishers_and_audit_actions
        .iter()
        .map(|v| v.0.id)
        .collect();

    let kws = CrateKeyword::belonging_to(&krate)
        .inner_join(keywords::table)
        .select(keywords::all_columns)
        .load(&*conn)?;
    let cats = CrateCategory::belonging_to(&krate)
        .inner_join(categories::table)
        .select(categories::all_columns)
        .load(&*conn)?;
    let recent_downloads = RecentCrateDownloads::belonging_to(&krate)
        .select(recent_crate_downloads::downloads)
        .get_result(&*conn)
        .optional()?;

    let badges = badges::table
        .filter(badges::crate_id.eq(krate.id))
        .load(&*conn)?;
    let top_versions = krate.top_versions(&conn)?;

    #[derive(Serialize)]
    struct R {
        #[serde(rename = "crate")]
        krate: EncodableCrate,
        versions: Vec<EncodableVersion>,
        keywords: Vec<EncodableKeyword>,
        categories: Vec<EncodableCategory>,
    }
    Ok(req.json(&R {
        krate: EncodableCrate::from(
            krate.clone(),
            &top_versions,
            Some(ids),
            Some(&kws),
            Some(&cats),
            Some(badges),
            false,
            recent_downloads,
        ),
        versions: versions_publishers_and_audit_actions
            .into_iter()
            .map(|(v, pb, aas)| EncodableVersion::from(v, &krate.name, pb, aas))
            .collect(),
        keywords: kws.into_iter().map(Keyword::into).collect(),
        categories: cats.into_iter().map(Category::into).collect(),
    }))
}

/// Handles the `GET /crates/:crate_id/:version/readme` route.
pub fn readme(req: &mut dyn RequestExt) -> EndpointResult {
    let crate_name = &req.params()["crate_id"];
    let version = &req.params()["version"];

    let redirect_url = req
        .app()
        .config
        .uploader()
        .readme_location(crate_name, version);

    if req.wants_json() {
        #[derive(Serialize)]
        struct R {
            url: String,
        }
        Ok(req.json(&R { url: redirect_url }))
    } else {
        Ok(req.redirect(redirect_url))
    }
}

/// Handles the `GET /crates/:crate_id/versions` route.
// FIXME: Not sure why this is necessary since /crates/:crate_id returns
// this information already, but ember is definitely requesting it
pub fn versions(req: &mut dyn RequestExt) -> EndpointResult {
    let crate_name = &req.params()["crate_id"];
    let conn = req.db_read_only()?;
    let krate: Crate = Crate::by_name(crate_name).first(&*conn)?;
    let mut versions_and_publishers: Vec<(Version, Option<User>)> = krate
        .all_versions()
        .left_outer_join(users::table)
        .select((versions::all_columns, users::all_columns.nullable()))
        .load(&*conn)?;

    versions_and_publishers
        .sort_by_cached_key(|(version, _)| Reverse(semver::Version::parse(&version.num).ok()));

    let versions = versions_and_publishers
        .iter()
        .map(|(v, _)| v)
        .cloned()
        .collect::<Vec<_>>();
    let versions = versions_and_publishers
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((v, pb), aas)| EncodableVersion::from(v, crate_name, pb, aas))
        .collect();

    #[derive(Serialize)]
    struct R {
        versions: Vec<EncodableVersion>,
    }
    Ok(req.json(&R { versions }))
}

/// Handles the `GET /crates/:crate_id/reverse_dependencies` route.
pub fn reverse_dependencies(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::any;

    let pagination_options = PaginationOptions::builder().gather(req)?;
    let name = &req.params()["crate_id"];
    let conn = req.db_read_only()?;
    let krate: Crate = Crate::by_name(name).first(&*conn)?;
    let (rev_deps, total) = krate.reverse_dependencies(&*conn, pagination_options)?;
    let rev_deps: Vec<_> = rev_deps
        .into_iter()
        .map(|dep| EncodableDependency::from_reverse_dep(dep, &krate.name))
        .collect();

    let version_ids: Vec<i32> = rev_deps.iter().map(|dep| dep.version_id).collect();

    let versions_and_publishers: Vec<(Version, String, Option<User>)> = versions::table
        .filter(versions::id.eq(any(version_ids)))
        .inner_join(crates::table)
        .left_outer_join(users::table)
        .select((
            versions::all_columns,
            crates::name,
            users::all_columns.nullable(),
        ))
        .load(&*conn)?;
    let versions = versions_and_publishers
        .iter()
        .map(|(v, _, _)| v)
        .cloned()
        .collect::<Vec<_>>();
    let versions = versions_and_publishers
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((version, krate_name, published_by), actions)| {
            EncodableVersion::from(version, &krate_name, published_by, actions)
        })
        .collect();

    #[derive(Serialize)]
    struct R {
        dependencies: Vec<EncodableDependency>,
        versions: Vec<EncodableVersion>,
        meta: Meta,
    }
    #[derive(Serialize)]
    struct Meta {
        total: i64,
    }
    Ok(req.json(&R {
        dependencies: rev_deps,
        versions,
        meta: Meta { total },
    }))
}
<commit_msg>controllers::krate::metadata: Use `json!` macro to simplify JSON serialization code<commit_after>//! Endpoints that expose metadata about a crate
//!
//! These endpoints provide data that could be obtained directly from the
//! index or cached metadata which was extracted (client side) from the
//! `Cargo.toml` file.

use std::cmp::Reverse;

use crate::controllers::frontend_prelude::*;
use crate::controllers::helpers::pagination::PaginationOptions;

use crate::models::{
    Category, Crate, CrateCategory, CrateKeyword, CrateVersions, Keyword, RecentCrateDownloads,
    TopVersions, User, Version, VersionOwnerAction,
};
use crate::schema::*;
use crate::views::{
    EncodableCategory, EncodableCrate, EncodableDependency, EncodableKeyword, EncodableVersion,
};

use crate::models::krate::ALL_COLUMNS;

/// Handles the `GET /summary` route.
pub fn summary(req: &mut dyn RequestExt) -> EndpointResult {
    use crate::schema::crates::dsl::*;

    let conn = req.db_read_only()?;
    let num_crates: i64 = crates.count().get_result(&*conn)?;
    let num_downloads: i64 = metadata::table
        .select(metadata::total_downloads)
        .get_result(&*conn)?;

    let encode_crates = |data: Vec<(Crate, Option<i64>)>| -> AppResult<Vec<_>> {
        let recent_downloads = data.iter().map(|&(_, s)| s).collect::<Vec<_>>();

        let krates = data.into_iter().map(|(c, _)| c).collect::<Vec<_>>();

        let versions: Vec<Version> = krates.versions().load(&*conn)?;
        versions
            .grouped_by(&krates)
            .into_iter()
            .map(TopVersions::from_versions)
            .zip(krates)
            .zip(recent_downloads)
            .map(|((top_versions, krate), recent_downloads)| {
                Ok(EncodableCrate::from_minimal(
                    krate,
                    &top_versions,
                    None,
                    false,
                    recent_downloads,
                ))
            })
            .collect()
    };

    let selection = (ALL_COLUMNS, recent_crate_downloads::downloads.nullable());

    let new_crates = crates
        .left_join(recent_crate_downloads::table)
        .order(created_at.desc())
        .select(selection)
        .limit(10)
        .load(&*conn)?;
    let just_updated = crates
        .left_join(recent_crate_downloads::table)
        .filter(updated_at.ne(created_at))
        .order(updated_at.desc())
        .select(selection)
        .limit(10)
        .load(&*conn)?;
    let most_downloaded = crates
        .left_join(recent_crate_downloads::table)
        .then_order_by(downloads.desc())
        .select(selection)
        .limit(10)
        .load(&*conn)?;

    let most_recently_downloaded = crates
        .inner_join(recent_crate_downloads::table)
        .then_order_by(recent_crate_downloads::downloads.desc())
        .select(selection)
        .limit(10)
        .load(&*conn)?;

    let popular_keywords = keywords::table
        .order(keywords::crates_cnt.desc())
        .limit(10)
        .load(&*conn)?
        .into_iter()
        .map(Keyword::into)
        .collect::<Vec<EncodableKeyword>>();

    let popular_categories = Category::toplevel(&conn, "crates", 10, 0)?
        .into_iter()
        .map(Category::into)
        .collect::<Vec<EncodableCategory>>();

    Ok(req.json(&json!({
        "num_downloads": num_downloads,
        "num_crates": num_crates,
        "new_crates": encode_crates(new_crates)?,
        "most_downloaded": encode_crates(most_downloaded)?,
        "most_recently_downloaded": encode_crates(most_recently_downloaded)?,
        "just_updated": encode_crates(just_updated)?,
        "popular_keywords": popular_keywords,
        "popular_categories": popular_categories,
    })))
}

/// Handles the `GET /crates/:crate_id` route.
pub fn show(req: &mut dyn RequestExt) -> EndpointResult {
    let name = &req.params()["crate_id"];
    let conn = req.db_read_only()?;
    let krate: Crate = Crate::by_name(name).first(&*conn)?;

    let mut versions_and_publishers: Vec<(Version, Option<User>)> = krate
        .all_versions()
        .left_outer_join(users::table)
        .select((versions::all_columns, users::all_columns.nullable()))
        .load(&*conn)?;

    versions_and_publishers
        .sort_by_cached_key(|(version, _)| Reverse(semver::Version::parse(&version.num).ok()));

    let versions = versions_and_publishers
        .iter()
        .map(|(v, _)| v)
        .cloned()
        .collect::<Vec<_>>();
    let versions_publishers_and_audit_actions = versions_and_publishers
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((v, pb), aas)| (v, pb, aas))
        .collect::<Vec<_>>();
    let ids = versions_publishers_and_audit_actions
        .iter()
        .map(|v| v.0.id)
        .collect();

    let kws = CrateKeyword::belonging_to(&krate)
        .inner_join(keywords::table)
        .select(keywords::all_columns)
        .load(&*conn)?;
    let cats = CrateCategory::belonging_to(&krate)
        .inner_join(categories::table)
        .select(categories::all_columns)
        .load(&*conn)?;
    let recent_downloads = RecentCrateDownloads::belonging_to(&krate)
        .select(recent_crate_downloads::downloads)
        .get_result(&*conn)
        .optional()?;

    let badges = badges::table
        .filter(badges::crate_id.eq(krate.id))
        .load(&*conn)?;
    let top_versions = krate.top_versions(&conn)?;

    Ok(req.json(&json!({
        "crate": EncodableCrate::from(
            krate.clone(),
            &top_versions,
            Some(ids),
            Some(&kws),
            Some(&cats),
            Some(badges),
            false,
            recent_downloads,
        ),
        "versions": versions_publishers_and_audit_actions
            .into_iter()
            .map(|(v, pb, aas)| EncodableVersion::from(v, &krate.name, pb, aas))
            .collect::<Vec<_>>(),
        "keywords": kws.into_iter().map(Keyword::into).collect::<Vec<EncodableKeyword>>(),
        "categories": cats.into_iter().map(Category::into).collect::<Vec<EncodableCategory>>(),
    })))
}

/// Handles the `GET /crates/:crate_id/:version/readme` route.
pub fn readme(req: &mut dyn RequestExt) -> EndpointResult {
    let crate_name = &req.params()["crate_id"];
    let version = &req.params()["version"];

    let redirect_url = req
        .app()
        .config
        .uploader()
        .readme_location(crate_name, version);

    if req.wants_json() {
        Ok(req.json(&json!({ "url": redirect_url })))
    } else {
        Ok(req.redirect(redirect_url))
    }
}

/// Handles the `GET /crates/:crate_id/versions` route.
// FIXME: Not sure why this is necessary since /crates/:crate_id returns
// this information already, but ember is definitely requesting it
pub fn versions(req: &mut dyn RequestExt) -> EndpointResult {
    let crate_name = &req.params()["crate_id"];
    let conn = req.db_read_only()?;
    let krate: Crate = Crate::by_name(crate_name).first(&*conn)?;
    let mut versions_and_publishers: Vec<(Version, Option<User>)> = krate
        .all_versions()
        .left_outer_join(users::table)
        .select((versions::all_columns, users::all_columns.nullable()))
        .load(&*conn)?;

    versions_and_publishers
        .sort_by_cached_key(|(version, _)| Reverse(semver::Version::parse(&version.num).ok()));

    let versions = versions_and_publishers
        .iter()
        .map(|(v, _)| v)
        .cloned()
        .collect::<Vec<_>>();
    let versions = versions_and_publishers
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((v, pb), aas)| EncodableVersion::from(v, crate_name, pb, aas))
        .collect::<Vec<_>>();

    Ok(req.json(&json!({ "versions": versions })))
}

/// Handles the `GET /crates/:crate_id/reverse_dependencies` route.
pub fn reverse_dependencies(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::any;

    let pagination_options = PaginationOptions::builder().gather(req)?;
    let name = &req.params()["crate_id"];
    let conn = req.db_read_only()?;
    let krate: Crate = Crate::by_name(name).first(&*conn)?;
    let (rev_deps, total) = krate.reverse_dependencies(&*conn, pagination_options)?;
    let rev_deps: Vec<_> = rev_deps
        .into_iter()
        .map(|dep| EncodableDependency::from_reverse_dep(dep, &krate.name))
        .collect();

    let version_ids: Vec<i32> = rev_deps.iter().map(|dep| dep.version_id).collect();

    let versions_and_publishers: Vec<(Version, String, Option<User>)> = versions::table
        .filter(versions::id.eq(any(version_ids)))
        .inner_join(crates::table)
        .left_outer_join(users::table)
        .select((
            versions::all_columns,
            crates::name,
            users::all_columns.nullable(),
        ))
        .load(&*conn)?;
    let versions = versions_and_publishers
        .iter()
        .map(|(v, _, _)| v)
        .cloned()
        .collect::<Vec<_>>();
    let versions = versions_and_publishers
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((version, krate_name, published_by), actions)| {
            EncodableVersion::from(version, &krate_name, published_by, actions)
        })
        .collect::<Vec<_>>();

    Ok(req.json(&json!({
        "dependencies": rev_deps,
        "versions": versions,
        "meta": { "total": total },
    })))
}
<|endoftext|>


<commit_before>use parking_lot::RwLock;
use std::any::Any;

use std::rc::Rc;

use super::Mode;

use crate::core::codepointinfo::CodepointInfo;

use crate::core::codepointinfo::TextStyle;
use crate::core::editor::register_input_stage_action;
use crate::core::editor::set_focus_on_vid;
use crate::core::editor::InputStageActionMap;
use crate::core::event::input_map::build_input_event_map;

use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::view;
use crate::core::view::ContentFilter;
use crate::core::view::FilterIo;
use crate::core::view::LayoutEnv;

use crate::core::view::View;

use crate::core::view::ViewEvent;
use crate::core::view::ViewEventDestination;
use crate::core::view::ViewEventSource;

use crate::core::event::*;

static VSCROLLBAR_INPUT_MAP: &str = r#"
[
  {
    "events": [
     { "in": [{ "pointer-motion": "" }], "action": "vscrollbar:input-event" },
     { "default": [],                    "action": "vscrollbar:input-event" }
   ]
  }

]"#;

pub struct VscrollbarMode {
    // add common fields
}
pub struct VscrollbarModeContext {
    pub target_vid: view::Id,

    pub percent: f64,
    pub percent_end: f64,
    pub scroll_start: usize,
    pub scroll_end: usize,
    pub selected: bool,
    pub t: std::time::Instant,
}

impl<'a> Mode for VscrollbarMode {
    fn name(&self) -> &'static str {
        &"vscrollbar-mode"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        let mut map = InputStageActionMap::new();
        Self::register_input_stage_actions(&mut map);
        map
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc vscrollbar-mode ctx");
        let ctx = VscrollbarModeContext {
            target_vid: view::Id(0),
            percent: 0.0,
            percent_end: 0.0,
            scroll_start: 0,
            scroll_end: 0,
            selected: false,
            t: std::time::Instant::now(),
        };
        Box::new(ctx)
    }

    fn configure_view(
        &mut self,
        _editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        view: &mut View<'static>,
    ) {
        // setup input map for core actions
        let input_map = build_input_event_map(VSCROLLBAR_INPUT_MAP).unwrap();
        let mut input_map_stack = view.input_ctx.input_map.as_ref().borrow_mut();
        input_map_stack.push((self.name(), input_map));

        view.compose_content_filters
            .borrow_mut()
            .push(Box::new(VscrollbarModeComposeFilter::new()));
    }

    fn on_view_event(
        &self,
        editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        src: ViewEventSource,
        dst: ViewEventDestination,
        event: &ViewEvent,
        src_view: &mut View<'static>,
        _parent: Option<&mut View<'static>>,
    ) {
        dbg_println!(
            "mode '{}' on_view_event src: {:?} dst: {:?}, event {:?} view.id {:?}",
            self.name(),
            src,
            dst,
            event,
            src_view.id
        );

        match event {
            ViewEvent::Subscribe => {
                // we subscribe to src events
                if src.id == dst.id {
                    return;
                }

                let mut dst = editor.view_map.get(&dst.id).unwrap().write();
                let mut mode_ctx = dst.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
                mode_ctx.target_vid = src.id;
            }

            ViewEvent::PreComposition => {
                let src = src_view;
                let dim = src.screen.read().dimension();

                let mut dst = editor.view_map.get(&dst.id).unwrap().write();
                let mut mode_ctx = dst.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");

                let doc = src.document.as_ref().unwrap();
                let doc = doc.read();
                let doc_size = doc.size();

                let off = src.start_offset as f64 / doc_size as f64;
                let off2 = src.end_offset as f64 / doc_size as f64;

                mode_ctx.percent = off * 100.0;
                mode_ctx.percent_end = off2 * 100.0;

                let height = dim.1 as f64 / 100.0;
                mode_ctx.scroll_start = (height * mode_ctx.percent) as usize;
                mode_ctx.scroll_start =
                    std::cmp::min(dim.1.saturating_sub(1), mode_ctx.scroll_start);
                mode_ctx.scroll_end = (height * mode_ctx.percent_end) as usize;
                mode_ctx.scroll_end = if mode_ctx.scroll_end == mode_ctx.scroll_start {
                    mode_ctx.scroll_start + 1
                } else {
                    mode_ctx.scroll_end
                };
                mode_ctx.scroll_end = std::cmp::min(dim.1, mode_ctx.scroll_end);

                dbg_println!("SCROLLBAR: mode_ctx.percent {}", mode_ctx.percent);
                dbg_println!("SCROLLBAR: mode_ctx.percent_end {}", mode_ctx.percent_end);
            }

            _ => {}
        }
    }
}

impl VscrollbarMode {
    pub fn new() -> Self {
        dbg_println!("VscrollbarMode");
        VscrollbarMode {}
    }

    pub fn register_input_stage_actions<'a>(mut map: &'a mut InputStageActionMap<'a>) {
        register_input_stage_action(&mut map, "vscrollbar:input-event", vscrollbar_input_event);
    }
}

// TODO?: mode:on_button_press(btn, x,y) ...
// TODO?: mode:on_button_release(btn ?) ...
// TODO?: mode:on_pointer_drag(btn, x,y)

pub fn vscrollbar_input_event(
    mut editor: &mut Editor<'static>,
    mut env: &mut EditorEnv<'static>,
    view: &Rc<RwLock<View>>,
) {
    let mut v = view.write();

    let evt = v.input_ctx.trigger.last();
    match evt {
        Some(InputEvent::ButtonPress(ref button_event)) => match button_event {
            ButtonEvent {
                mods:
                    KeyModifiers {
                        ctrl: _,
                        alt: _,
                        shift: _,
                    },
                x,
                y,
                button,
            } => {
                dbg_println!("VSCROLLBAR btn press evt {} {}x{}", button, x, y);

                if *button == 0 {
                    let y = *y as usize;
                    let mut mode_ctx = v.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
                    if y >= mode_ctx.scroll_start && y < mode_ctx.scroll_end {
                        mode_ctx.selected = true;
                        env.focus_locked_on = Some(v.id);
                    }

                    return;
                }
            }
        },

        Some(InputEvent::ButtonRelease(ref button_event)) => match button_event {
            ButtonEvent {
                mods:
                    KeyModifiers {
                        ctrl: _,
                        alt: _,
                        shift: _,
                    },
                x,
                y,
                button,
            } => {
                dbg_println!("VSCROLLBAR btn release evt {} {}x{}", button, x, y);

                if *button == 0 {
                    let mode_ctx = v.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
                    mode_ctx.selected = false;
                    env.focus_locked_on = None;

                    // explicit focus on target view
                    set_focus_on_vid(&mut editor, &mut env, mode_ctx.target_vid);
                }
            }
        },

        Some(InputEvent::PointerMotion(PointerEvent { x, y, mods: _ })) => {
            dbg_println!("VSCROLLBAR CLIPPING x {} y {}", x, y);
            let target_vid = {
                let mode_ctx = v.mode_ctx::<VscrollbarModeContext>("vscrollbar-mode");

                // explicit focus on target view
                // set_focus_on_vid(&mut editor, &mut env, mode_ctx.target_vid);

                if !mode_ctx.selected {
                    return;
                }

                if mode_ctx.target_vid == view::Id(0) {
                    return;
                }

                mode_ctx.target_vid
            };

            let dim = v.screen.read().dimension();
            let mut dst = editor.view_map.get(&target_vid).unwrap().write();

            let doc_size = {
                let doc = dst.document.as_ref().unwrap();
                let doc = doc.read();
                std::cmp::max(1, doc.size()) // avoid div by zero
            };

            let y = std::cmp::max(0, *y) as usize; //  coordinates can be negative

            let y = if y < dim.1.saturating_sub(1) {
                y
            } else {
                dim.1.saturating_sub(1)
            };

            let percent = y as f64 / dim.1 as f64;
            let percent = if y < dim.1.saturating_sub(1) {
                percent
            } else {
                1.0
            };

            dbg_println!("SCROLLBAR H: {}", dim.1);
            dbg_println!("SCROLLBAR Y: {}", y);
            dbg_println!("SCROLLBAR percent: {}", percent);

            // set target's offset
            // the scrollbar dimension are recomputed in on_view_event
            let offset = (doc_size as f64 * percent) as u64;
            dst.start_offset = offset;

            // TODO(ceg): push action
            // center + scroll-down (h / 2)

            // TODO(ceg): update scrollbar.subscription()
            // view.on_event(ViewEvent { ChangeOffset{ offset } });
            // view.on_request(src, dsc, ViewEvent { ChangeOffset{ offset } });
        }

        _ => {
            dbg_println!("VSCROLLBAR unhandled event {:?}", evt);
            return;
        }
    };

    // TODO(ceg): move scrollbar
    {
        let mode_ctx = v.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
        if !mode_ctx.selected {
            return;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

pub struct VscrollbarModeComposeFilter {}

impl VscrollbarModeComposeFilter {
    pub fn new() -> Self {
        VscrollbarModeComposeFilter {}
    }
}

impl ContentFilter<'_> for VscrollbarModeComposeFilter {
    fn name(&self) -> &'static str {
        &"vscrollbar-compose-filter"
    }

    fn run(
        &mut self,
        view: &View,
        env: &mut LayoutEnv,
        _filter_in: &[FilterIo],
        _filter_out: &mut Vec<FilterIo>,
    ) {
        let mode_ctx = view.mode_ctx::<VscrollbarModeContext>("vscrollbar-mode");
        let mut cpi = CodepointInfo::new();

        cpi.style.bg_color = (45, 49, 54);

        cpi.style.bg_color = TextStyle::default_bg_color();

        cpi.cp = ' ';
        cpi.displayed_cp = ' ';
        loop {
            let (b, _) = env.screen.push(cpi.clone());
            if !b {
                break;
            }
        }

        dbg_println!("SCROLLBAR height {}", env.screen.height());
        dbg_println!("SCROLLBAR start {}", mode_ctx.scroll_start);
        dbg_println!("SCROLLBAR end {}", mode_ctx.scroll_end);

        for i in mode_ctx.scroll_start..mode_ctx.scroll_end {
            if let Some(cpi) = env.screen.get_cpinfo_mut(0, i) {
                cpi.displayed_cp = ' ';
                cpi.style.is_selected = true;
                let add = 25;
                //                cpi.style.bg_color = (45 + add, 49 + add, 54 + add);
                cpi.style.bg_color = (45 - add, 49 - add, 54 - add);

                cpi.style.bg_color = (188, 188, 188);

                if mode_ctx.selected {
                    cpi.style.bg_color = TextStyle::default_color();
                } else {
                    let add = 25;
                    //                    cpi.style.bg_color = (45 + add, 49 + add, 54 + add);
                    cpi.style.bg_color = (45 - add, 49 - add, 54 - add);
                    cpi.style.bg_color = (188, 188, 188);
                }
            }
        }

        env.quit = true;
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {}
}
<commit_msg>work on scrollbar colors<commit_after>use parking_lot::RwLock;
use std::any::Any;

use std::rc::Rc;

use super::Mode;

use crate::core::codepointinfo::CodepointInfo;

use crate::core::codepointinfo::TextStyle;
use crate::core::editor::register_input_stage_action;
use crate::core::editor::set_focus_on_vid;
use crate::core::editor::InputStageActionMap;
use crate::core::event::input_map::build_input_event_map;

use crate::core::Editor;
use crate::core::EditorEnv;

use crate::core::view;
use crate::core::view::ContentFilter;
use crate::core::view::FilterIo;
use crate::core::view::LayoutEnv;

use crate::core::view::View;

use crate::core::view::ViewEvent;
use crate::core::view::ViewEventDestination;
use crate::core::view::ViewEventSource;

use crate::core::event::*;

static VSCROLLBAR_INPUT_MAP: &str = r#"
[
  {
    "events": [
     { "in": [{ "pointer-motion": "" }], "action": "vscrollbar:input-event" },
     { "default": [],                    "action": "vscrollbar:input-event" }
   ]
  }

]"#;

pub struct VscrollbarMode {
    // add common fields
}
pub struct VscrollbarModeContext {
    pub target_vid: view::Id,

    pub percent: f64,
    pub percent_end: f64,
    pub scroll_start: usize,
    pub scroll_end: usize,
    pub selected: bool,
    pub pointer_over: bool,
    pub t: std::time::Instant,
}

impl<'a> Mode for VscrollbarMode {
    fn name(&self) -> &'static str {
        &"vscrollbar-mode"
    }

    fn build_action_map(&self) -> InputStageActionMap<'static> {
        let mut map = InputStageActionMap::new();
        Self::register_input_stage_actions(&mut map);
        map
    }

    fn alloc_ctx(&self) -> Box<dyn Any> {
        dbg_println!("alloc vscrollbar-mode ctx");
        let ctx = VscrollbarModeContext {
            target_vid: view::Id(0),
            percent: 0.0,
            percent_end: 0.0,
            scroll_start: 0,
            scroll_end: 0,
            selected: false,
            pointer_over: false,
            t: std::time::Instant::now(),
        };
        Box::new(ctx)
    }

    fn configure_view(
        &mut self,
        _editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        view: &mut View<'static>,
    ) {
        // setup input map for core actions
        let input_map = build_input_event_map(VSCROLLBAR_INPUT_MAP).unwrap();
        let mut input_map_stack = view.input_ctx.input_map.as_ref().borrow_mut();
        input_map_stack.push((self.name(), input_map));

        view.compose_content_filters
            .borrow_mut()
            .push(Box::new(VscrollbarModeComposeFilter::new()));
    }

    fn on_view_event(
        &self,
        editor: &mut Editor<'static>,
        _env: &mut EditorEnv<'static>,
        src: ViewEventSource,
        dst: ViewEventDestination,
        event: &ViewEvent,
        src_view: &mut View<'static>,
        _parent: Option<&mut View<'static>>,
    ) {
        dbg_println!(
            "mode '{}' on_view_event src: {:?} dst: {:?}, event {:?} view.id {:?}",
            self.name(),
            src,
            dst,
            event,
            src_view.id
        );

        match event {
            ViewEvent::Subscribe => {
                // we subscribe to src events
                if src.id == dst.id {
                    return;
                }

                let mut dst = editor.view_map.get(&dst.id).unwrap().write();
                let mut mode_ctx = dst.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
                mode_ctx.target_vid = src.id;
            }

            ViewEvent::PreComposition => {
                let src = src_view;
                let dim = src.screen.read().dimension();

                let mut dst = editor.view_map.get(&dst.id).unwrap().write();
                let mut mode_ctx = dst.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");

                let doc = src.document.as_ref().unwrap();
                let doc = doc.read();
                let doc_size = doc.size();

                let off = src.start_offset as f64 / doc_size as f64;
                let off2 = src.end_offset as f64 / doc_size as f64;

                mode_ctx.percent = off * 100.0;
                mode_ctx.percent_end = off2 * 100.0;

                let height = dim.1 as f64 / 100.0;
                mode_ctx.scroll_start = (height * mode_ctx.percent) as usize;
                mode_ctx.scroll_start =
                    std::cmp::min(dim.1.saturating_sub(1), mode_ctx.scroll_start);
                mode_ctx.scroll_end = (height * mode_ctx.percent_end) as usize;
                mode_ctx.scroll_end = if mode_ctx.scroll_end == mode_ctx.scroll_start {
                    mode_ctx.scroll_start + 1
                } else {
                    mode_ctx.scroll_end
                };
                mode_ctx.scroll_end = std::cmp::min(dim.1, mode_ctx.scroll_end);

                dbg_println!("SCROLLBAR: mode_ctx.percent {}", mode_ctx.percent);
                dbg_println!("SCROLLBAR: mode_ctx.percent_end {}", mode_ctx.percent_end);
            }

            ViewEvent::Enter => {
                let mut mode_ctx =
                    src_view.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
                mode_ctx.pointer_over = true;
            }

            ViewEvent::Leave => {
                let mut mode_ctx =
                    src_view.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
                mode_ctx.pointer_over = false;
            }

            _ => {}
        }
    }
}

impl VscrollbarMode {
    pub fn new() -> Self {
        dbg_println!("VscrollbarMode");
        VscrollbarMode {}
    }

    pub fn register_input_stage_actions<'a>(mut map: &'a mut InputStageActionMap<'a>) {
        register_input_stage_action(&mut map, "vscrollbar:input-event", vscrollbar_input_event);
    }
}

// TODO?: mode:on_button_press(btn, x,y) ...
// TODO?: mode:on_button_release(btn ?) ...
// TODO?: mode:on_pointer_drag(btn, x,y)

pub fn vscrollbar_input_event(
    mut editor: &mut Editor<'static>,
    mut env: &mut EditorEnv<'static>,
    view: &Rc<RwLock<View>>,
) {
    let mut v = view.write();

    let evt = v.input_ctx.trigger.last();
    match evt {
        Some(InputEvent::ButtonPress(ref button_event)) => match button_event {
            ButtonEvent {
                mods:
                    KeyModifiers {
                        ctrl: _,
                        alt: _,
                        shift: _,
                    },
                x,
                y,
                button,
            } => {
                dbg_println!("VSCROLLBAR btn press evt {} {}x{}", button, x, y);

                if *button == 0 {
                    let y = *y as usize;
                    let mut mode_ctx = v.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
                    if y >= mode_ctx.scroll_start && y < mode_ctx.scroll_end {
                        mode_ctx.selected = true;
                        env.focus_locked_on = Some(v.id);
                    }

                    return;
                }
            }
        },

        Some(InputEvent::ButtonRelease(ref button_event)) => match button_event {
            ButtonEvent {
                mods:
                    KeyModifiers {
                        ctrl: _,
                        alt: _,
                        shift: _,
                    },
                x,
                y,
                button,
            } => {
                dbg_println!("VSCROLLBAR btn release evt {} {}x{}", button, x, y);

                if *button == 0 {
                    let mode_ctx = v.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
                    mode_ctx.selected = false;
                    env.focus_locked_on = None;

                    // explicit focus on target view
                    set_focus_on_vid(&mut editor, &mut env, mode_ctx.target_vid);
                }
            }
        },

        Some(InputEvent::PointerMotion(PointerEvent { x, y, mods: _ })) => {
            dbg_println!("VSCROLLBAR CLIPPING x {} y {}", x, y);
            let target_vid = {
                let mode_ctx = v.mode_ctx::<VscrollbarModeContext>("vscrollbar-mode");

                // explicit focus on target view
                // set_focus_on_vid(&mut editor, &mut env, mode_ctx.target_vid);

                if !mode_ctx.selected {
                    return;
                }

                if mode_ctx.target_vid == view::Id(0) {
                    return;
                }

                mode_ctx.target_vid
            };

            let dim = v.screen.read().dimension();
            let mut dst = editor.view_map.get(&target_vid).unwrap().write();

            let doc_size = {
                let doc = dst.document.as_ref().unwrap();
                let doc = doc.read();
                std::cmp::max(1, doc.size()) // avoid div by zero
            };

            let y = std::cmp::max(0, *y) as usize; //  coordinates can be negative

            let y = if y < dim.1.saturating_sub(1) {
                y
            } else {
                dim.1.saturating_sub(1)
            };

            let percent = y as f64 / dim.1 as f64;
            let percent = if y < dim.1.saturating_sub(1) {
                percent
            } else {
                1.0
            };

            dbg_println!("SCROLLBAR H: {}", dim.1);
            dbg_println!("SCROLLBAR Y: {}", y);
            dbg_println!("SCROLLBAR percent: {}", percent);

            // set target's offset
            // the scrollbar dimension are recomputed in on_view_event
            let offset = (doc_size as f64 * percent) as u64;
            dst.start_offset = offset;

            // TODO(ceg): push action
            // center + scroll-down (h / 2)

            // TODO(ceg): update scrollbar.subscription()
            // view.on_event(ViewEvent { ChangeOffset{ offset } });
            // view.on_request(src, dsc, ViewEvent { ChangeOffset{ offset } });
        }

        _ => {
            dbg_println!("VSCROLLBAR unhandled event {:?}", evt);
            return;
        }
    };

    // TODO(ceg): move scrollbar
    {
        let mode_ctx = v.mode_ctx_mut::<VscrollbarModeContext>("vscrollbar-mode");
        if !mode_ctx.selected {
            return;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

pub struct VscrollbarModeComposeFilter {}

impl VscrollbarModeComposeFilter {
    pub fn new() -> Self {
        VscrollbarModeComposeFilter {}
    }
}

impl ContentFilter<'_> for VscrollbarModeComposeFilter {
    fn name(&self) -> &'static str {
        &"vscrollbar-compose-filter"
    }

    fn run(
        &mut self,
        view: &View,
        env: &mut LayoutEnv,
        _filter_in: &[FilterIo],
        _filter_out: &mut Vec<FilterIo>,
    ) {
        let mode_ctx = view.mode_ctx::<VscrollbarModeContext>("vscrollbar-mode");
        let mut cpi = CodepointInfo::new();

        //        cpi.style.bg_color = (45, 49, 54);
        //        cpi.style.color = (192,0,0);

        //        cpi.style.bg_color = TextStyle::default_bg_color();

        // TextStyle::scrollbar_non_selected_color();
        // TextStyle::scrollbar_selected_color();
        // TextStyle::scrollbar_hover_color();

        cpi.cp = ' ';
        cpi.displayed_cp = ' ';
        loop {
            let (b, _) = env.screen.push(cpi.clone());
            if !b {
                break;
            }
        }

        // by default the scrollbar is fill with invisible ' ' and default fg/bg color
        // fill whole scrollbar bg
        // let h = env.screen.height();
        // for i in 0..h {
        //     if let Some(cpi) = env.screen.get_cpinfo_mut(0, i) {
        //         cpi.displayed_cp = ' ';
        //     }
        // }

        dbg_println!("SCROLLBAR height {}", env.screen.height());
        dbg_println!("SCROLLBAR start {}", mode_ctx.scroll_start);
        dbg_println!("SCROLLBAR end {}", mode_ctx.scroll_end);

        for i in mode_ctx.scroll_start..mode_ctx.scroll_end {
            if let Some(cpi) = env.screen.get_cpinfo_mut(0, i) {
                cpi.displayed_cp = ' ';

                if mode_ctx.selected {
                    cpi.style.bg_color = (34,167,242);
                } else {
                    if mode_ctx.pointer_over {
                        cpi.style.bg_color = (0,119,184);

                    } else {
                        cpi.style.bg_color = (51, 60, 98);
                        cpi.style.bg_color = (31, 36, 59);
                    }
                }
            }
        }

        env.quit = true;
    }

    fn finish(&mut self, _view: &View, _env: &mut LayoutEnv) {}
}
<|endoftext|>


<commit_before>pub mod basic_particle;
pub use self::basic_particle::*;

extern crate num;
use self::num::Float;
use super::vector::*;

//An object that has quanta
pub trait Quanta<D> {
    //Retrieve the quanta of a physics particle
    fn quanta(&self) -> D;
}

//An object that has inertia
pub trait Inertia<D> {
    //Retrieve the inertia of a physics particle
    fn inertia(&self) -> D;
}

//An opject with a location
pub trait Position<V> {
    //Get position of particle
    fn position(&self) -> V;
}

//An object that has a simple particle motion interface
pub trait Particle<V, D>: Position<V> + Inertia<D> {
    //Get velocity of particle
    fn velocity(&self) -> V;
    //Accelerate particle
    fn accelerate(&mut self, vec: &V);
    //Advance particle (update position and velocity)
    fn advance(&mut self, time: D);
}

pub trait PhysicsParticle<V, D>: Particle<V, D> + Quanta<D> + Inertia<D>
    where V: Vector<D>, D: Float
{
    //Convert a PhysicsParticle into a "basic particle" that implements PhysicsParticle but has the minimum members
    fn basic_form(&self) -> BasicParticle<V, D> {
        BasicParticle::new(self.quanta(), self.position(), self.velocity(), self.inertia())
    }

    //Apply drag forces to a particle
    fn drag(&mut self, magnitude: D) {
        let acceleration = -self.velocity() * magnitude / self.inertia();
        self.accelerate(&acceleration);
    }

    //Apply proper attraction between two physics particles based on their quanta and position
    fn gravitate<T: ?Sized>(lhs: &mut Self, rhs: &mut T, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        //Create delta vector between the two positions
        let delta = rhs.position() - lhs.position();
        //Find the distance of the delta vector
        let distance = delta.displacement();
        //Dividing the delta vector by the distance cubed computes attractive force as per the inverse square law.
        //The extra degree normalizes the direction vector to a unit vector.
        let force = delta * magnitude / distance.powi(3) *
        //Multiply by the two quanta of the objects to compute the final force.
        lhs.quanta() * rhs.quanta();

        //Accelerate lhs in the direction of force and divide it by the inertia
        let acceleration = force / lhs.inertia();
        lhs.accelerate(&acceleration);
        //Apply the inverse force to the rhs similarly
        let acceleration = -force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    /*
    If gravitating at a distance greater than the radius, then gravitational interaction is applied as if the
    particles are point particles. If the distance is less than the radius, then the interaction happens as if the
    gravitational quanta (mass) is evenly distributed and gravitational flux is used instead, which causes the
    interaction to become proportional to the radius, meaning that as the radius approaches zero, so does the force.

    The radius is passed squared for internal efficiency reasons.
    */
    fn gravitate_radius_squared<T: ?Sized>(lhs: &mut Self, rhs: &mut T, radius_squared: D, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let distance_squared = delta.displacement_squared();
        //Force is the only thing that changes from normal gravitation
        let force = delta * magnitude * lhs.quanta() * rhs.quanta() / if distance_squared > radius_squared {
            distance_squared.sqrt().powi(3)
        } else {
            radius_squared
        };
        let acceleration = force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = -force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //This function exists so that if an optimization is possible later, it can be specially implemented.
    //Presently it just acts as a frontend to its squared counterpart. Prefer squared version if possible.
    fn gravitate_radius<T: ?Sized>(lhs: &mut Self, rhs: &mut T, radius: D, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        Self::gravitate_radius_squared(lhs, rhs, radius * radius, magnitude);
    }

    //Apply proper attraction to a single physics particle towards a location and with a magnitude
    fn gravitate_to(&mut self, location: V, magnitude: D) {
        //Create delta vector from the particle to the center of attraction
        let delta = location - self.position();
        let distance = delta.displacement();
        let acceleration = delta / distance.powi(3) * magnitude * self.quanta() / self.inertia();
        self.accelerate(&acceleration);
    }

    //Works the same as gravitate_radius_squared and gravitate_to
    fn gravitate_radius_squared_to(&mut self, location: V, radius_squared: D, magnitude: D) {
        //Create delta vector from the particle to the center of attraction
        let delta = location - self.position();
        let distance_squared = delta.displacement_squared();
        let acceleration = delta * magnitude * self.quanta() / (
                self.inertia() *
                if distance_squared > radius_squared {
                    distance_squared.sqrt().powi(3)
                } else {
                    radius_squared
                }
            );
        self.accelerate(&acceleration);
    }

    //This function exists so that if an optimization is possible later, it can be specially implemented.
    //Presently it just acts as a frontend to its squared counterpart. Prefer squared version if possible.
    fn gravitate_radius_to(&mut self, location: V, radius: D, magnitude: D) {
        self.gravitate_radius_squared_to(location, radius * radius, magnitude);
    }

    //Apply spring forces between two particles
    fn hooke<T: ?Sized>(lhs: &mut Self, rhs: &mut T, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let force = delta.normalized() * magnitude * delta.displacement() * lhs.quanta() * rhs.quanta();
        let acceleration = force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = -force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //Apply spring forces between two particles with specified equilibrium distance
    fn hooke_equilibrium<T: ?Sized>(lhs: &mut Self, rhs: &mut T, equilibrium: D, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let force = delta.normalized() * magnitude *
            //We scale such that if displacement is greater than equilibrium, the particles attract proportionally
            //Particles with a displacement smaller than equilibrium repel each other
            (delta.displacement() - equilibrium) *
            lhs.quanta() * rhs.quanta();
        let acceleration = force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = -force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //Apply spring forces between one particle and a position with a magnitude
    fn hooke_to(&mut self, location: V, magnitude: D) {
        let delta = location - self.position();
        let acceleration = delta.normalized() * delta.displacement() *
            magnitude * self.quanta() / self.inertia();
        self.accelerate(&acceleration);
    }

    //Apply spring forces between one particle and a position with an equilibrium and magnitude
    fn hooke_equilibrium_to(&mut self, location: V, equilibrium: D, magnitude: D) {
        let delta = location - self.position();
        let acceleration = delta.normalized() * (delta.displacement() - equilibrium) *
            magnitude * self.quanta() / self.inertia();
        self.accelerate(&acceleration);
    }

    //Apply lorentz forces between two PhysicsParticle objects based on quanta, position, and velocity
    fn lorentz<T: ?Sized>(lhs: &mut Self, rhs: &mut T, magnitude: D)
        where V: CrossVector, T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let distance = delta.displacement();
        let force = V::cross(&rhs.velocity(), &V::cross(&lhs.velocity(), &delta)) *
            lhs.quanta() * rhs.quanta() / distance.powi(3) * magnitude;

        let acceleration = -force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //Apply lorentz forces between two PhysicsParticle objects with a radius_squared
    fn lorentz_radius_squared<T: ?Sized>(lhs: &mut Self, rhs: &mut T, radius_squared: D, magnitude: D)
        where V: CrossVector, T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let distance_squared = delta.displacement_squared();
        let force = V::cross(&rhs.velocity(), &V::cross(&lhs.velocity(), &delta)) * magnitude *
            lhs.quanta() * rhs.quanta() /
            if distance_squared > radius_squared {
                distance_squared.sqrt().powi(3)
            } else {
                radius_squared
            };

        let acceleration = -force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //This function exists so that if an optimization is possible later, it can be specially implemented.
    //Presently it just acts as a frontend to its squared counterpart. Prefer squared version if possible.
    fn lorentz_radius<T: ?Sized>(lhs: &mut Self, rhs: &mut T, radius: D, magnitude: D)
        where V: CrossVector, T: PhysicsParticle<V, D>
    {
        Self::lorentz_radius_squared(lhs, rhs, radius * radius, magnitude);
    }

    //Apply lorentz force to a particle in a field given by a vector with the magnitude and direction of the field
    fn lorentz_field(&mut self, field: &V)
        where V: CrossVector
    {
        let acceleration = V::cross(&self.velocity(), field) * self.quanta() / self.inertia();
        self.accelerate(&acceleration);
    }
}
<commit_msg>initial vadix dev<commit_after>pub mod basic_particle;
pub use self::basic_particle::*;

extern crate num;
use self::num::Float;
use super::vector::*;

//An object that has quanta
pub trait Quanta<D> {
    //Retrieve the quanta of a physics particle
    fn quanta(&self) -> D;
}

//An object that has inertia
pub trait Inertia<D> {
    //Retrieve the inertia of a physics particle
    fn inertia(&self) -> D;
}

//An object with a location
pub trait Position<V> {
    //Get position of particle
    fn position(&self) -> V;
}

//A uniform sphere with a radius
pub trait UniformBall<D> {
    //Get radius of particle
    fn radius(&self) -> D;
    //Get radius squared of particle (override this when more efficient)
    fn radius_squared(&self) -> D
        where D: Float
    {
        self.radius().powi(2)
    }
}

//An object that has a simple particle motion interface
pub trait Particle<V, D>: Position<V> + Inertia<D> {
    //Get velocity of particle
    fn velocity(&self) -> V;
    //Accelerate particle
    fn accelerate(&mut self, vec: &V);
    //Advance particle (update position and velocity)
    fn advance(&mut self, time: D);
}

pub trait PhysicsParticle<V, D>: Particle<V, D> + Quanta<D> + Inertia<D>
    where V: Vector<D>, D: Float
{
    //Convert a PhysicsParticle into a "basic particle" that implements PhysicsParticle but has the minimum members
    fn basic_form(&self) -> BasicParticle<V, D> {
        BasicParticle::new(self.quanta(), self.position(), self.velocity(), self.inertia())
    }

    //Apply drag forces to a particle
    fn drag(&mut self, magnitude: D) {
        let acceleration = -self.velocity() * magnitude / self.inertia();
        self.accelerate(&acceleration);
    }

    //Apply proper attraction between two physics particles based on their quanta and position
    fn gravitate<T: ?Sized>(lhs: &mut Self, rhs: &mut T, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        //Create delta vector between the two positions
        let delta = rhs.position() - lhs.position();
        //Find the distance of the delta vector
        let distance = delta.displacement();
        //Dividing the delta vector by the distance cubed computes attractive force as per the inverse square law.
        //The extra degree normalizes the direction vector to a unit vector.
        let force = delta * magnitude / distance.powi(3) *
        //Multiply by the two quanta of the objects to compute the final force.
        lhs.quanta() * rhs.quanta();

        //Accelerate lhs in the direction of force and divide it by the inertia
        let acceleration = force / lhs.inertia();
        lhs.accelerate(&acceleration);
        //Apply the inverse force to the rhs similarly
        let acceleration = -force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    /*
    If gravitating at a distance greater than the radius, then gravitational interaction is applied as if the
    particles are point particles. If the distance is less than the radius, then the interaction happens as if the
    gravitational quanta (mass) is evenly distributed and gravitational flux is used instead, which causes the
    interaction to become proportional to the radius, meaning that as the radius approaches zero, so does the force.

    The radius is passed squared for internal efficiency reasons.
    */
    fn gravitate_radius_squared<T: ?Sized>(lhs: &mut Self, rhs: &mut T, radius_squared: D, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let distance_squared = delta.displacement_squared();
        //Force is the only thing that changes from normal gravitation
        let force = delta * magnitude * lhs.quanta() * rhs.quanta() / if distance_squared > radius_squared {
            distance_squared.sqrt().powi(3)
        } else {
            radius_squared
        };
        let acceleration = force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = -force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //This function exists so that if an optimization is possible later, it can be specially implemented.
    //Presently it just acts as a frontend to its squared counterpart. Prefer squared version if possible.
    fn gravitate_radius<T: ?Sized>(lhs: &mut Self, rhs: &mut T, radius: D, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        Self::gravitate_radius_squared(lhs, rhs, radius * radius, magnitude);
    }

    //Apply proper attraction to a single physics particle towards a location and with a magnitude
    fn gravitate_to<T: ?Sized>(&mut self, center: &T, magnitude: D)
        where T: Quanta<D> + Position<V>
    {
        //Create delta vector from the particle to the center of attraction
        let delta = center.position() - self.position();
        let distance = delta.displacement();
        let acceleration = delta / distance.powi(3) * magnitude *
            self.quanta() * center.quanta() / self.inertia();
        self.accelerate(&acceleration);
    }

    //Works the same as gravitate_radius_squared and gravitate_to
    fn gravitate_radius_to<T: ?Sized>(&mut self, center: &T, magnitude: D)
        where T: Quanta<D> + Position<V> + UniformBall<D>
    {
        //Create delta vector from the particle to the center of attraction
        let delta = center.position() - self.position();
        let distance_squared = delta.displacement_squared();
        let acceleration = delta * magnitude * self.quanta() * center.quanta() / (
                self.inertia() *
                if distance_squared > center.radius_squared() {
                    distance_squared.sqrt().powi(3)
                } else {
                    center.radius_squared()
                }
            );
        self.accelerate(&acceleration);
    }

    //Apply spring forces between two particles
    fn hooke<T: ?Sized>(lhs: &mut Self, rhs: &mut T, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let force = delta.normalized() * magnitude * delta.displacement() * lhs.quanta() * rhs.quanta();
        let acceleration = force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = -force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //Apply spring forces between two particles with specified equilibrium distance
    fn hooke_equilibrium<T: ?Sized>(lhs: &mut Self, rhs: &mut T, equilibrium: D, magnitude: D)
        where T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let force = delta.normalized() * magnitude *
            //We scale such that if displacement is greater than equilibrium, the particles attract proportionally
            //Particles with a displacement smaller than equilibrium repel each other
            (delta.displacement() - equilibrium) *
            lhs.quanta() * rhs.quanta();
        let acceleration = force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = -force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //Apply spring forces between one particle and a position with a magnitude
    fn hooke_to(&mut self, location: V, magnitude: D) {
        let delta = location - self.position();
        let acceleration = delta.normalized() * delta.displacement() *
            magnitude * self.quanta() / self.inertia();
        self.accelerate(&acceleration);
    }

    //Apply spring forces between one particle and a position with an equilibrium and magnitude
    fn hooke_equilibrium_to(&mut self, location: V, equilibrium: D, magnitude: D) {
        let delta = location - self.position();
        let acceleration = delta.normalized() * (delta.displacement() - equilibrium) *
            magnitude * self.quanta() / self.inertia();
        self.accelerate(&acceleration);
    }

    //Apply lorentz forces between two PhysicsParticle objects based on quanta, position, and velocity
    fn lorentz<T: ?Sized>(lhs: &mut Self, rhs: &mut T, magnitude: D)
        where V: CrossVector, T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let distance = delta.displacement();
        let force = V::cross(&rhs.velocity(), &V::cross(&lhs.velocity(), &delta)) *
            lhs.quanta() * rhs.quanta() / distance.powi(3) * magnitude;

        let acceleration = -force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //Apply lorentz forces between two PhysicsParticle objects with a radius_squared
    fn lorentz_radius_squared<T: ?Sized>(lhs: &mut Self, rhs: &mut T, radius_squared: D, magnitude: D)
        where V: CrossVector, T: PhysicsParticle<V, D>
    {
        let delta = rhs.position() - lhs.position();
        let distance_squared = delta.displacement_squared();
        let force = V::cross(&rhs.velocity(), &V::cross(&lhs.velocity(), &delta)) * magnitude *
            lhs.quanta() * rhs.quanta() /
            if distance_squared > radius_squared {
                distance_squared.sqrt().powi(3)
            } else {
                radius_squared
            };

        let acceleration = -force / lhs.inertia();
        lhs.accelerate(&acceleration);
        let acceleration = force / rhs.inertia();
        rhs.accelerate(&acceleration);
    }

    //This function exists so that if an optimization is possible later, it can be specially implemented.
    //Presently it just acts as a frontend to its squared counterpart. Prefer squared version if possible.
    fn lorentz_radius<T: ?Sized>(lhs: &mut Self, rhs: &mut T, radius: D, magnitude: D)
        where V: CrossVector, T: PhysicsParticle<V, D>
    {
        Self::lorentz_radius_squared(lhs, rhs, radius * radius, magnitude);
    }

    //Apply lorentz force to a particle in a field given by a vector with the magnitude and direction of the field
    fn lorentz_field(&mut self, field: &V)
        where V: CrossVector
    {
        let acceleration = V::cross(&self.velocity(), field) * self.quanta() / self.inertia();
        self.accelerate(&acceleration);
    }
}
<|endoftext|>


<commit_before>use cgmath;
use character::controls::CharacterInputState;
use critter::{CharacterSprite, CritterData};
use data;
use game::constants::{ASPECT_RATIO, CHARACTER_SHEET_TOTAL_WIDTH, RUN_SPRITE_OFFSET, SPRITE_OFFSET};
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::mouse_controls::MouseInputState;
use graphics::{camera::CameraInputState, Dimensions, get_orientation_from_center, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::prelude::{Read, ReadStorage, WriteStorage};
use zombie::{ZombieDrawable, zombies::Zombies};

pub mod controls;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Clone)]
pub struct CharacterDrawable {
  projection: Projection,
  pub position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
}

impl CharacterDrawable {
  pub fn new() -> CharacterDrawable {
    let view = Dimensions::get_view_matrix();
    CharacterDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(cgmath::Deg(75.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position: Position::new([0.0, 0.0]),
      orientation: Orientation::Right,
      stance: Stance::Walking,
      direction: Orientation::Right,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, mouse_input: &MouseInputState, dimensions: &Dimensions, zombies: &Vec<ZombieDrawable>) {
    self.projection = *world_to_clip;

    if zombies.iter()
              .any(|z| self.stance != Stance::NormalDeath &&
                overlaps(Position::new([ci.x_movement, ci.y_movement]), z.position, 20.0, 20.0)) {
      self.stance = Stance::NormalDeath;
      println!("Player died, press Esc to quit");
    }

    if self.stance != Stance::NormalDeath {
      if ci.is_shooting && mouse_input.left_click_point.is_some() && !ci.is_colliding {
        self.stance = Stance::Firing;
        self.orientation = get_orientation_from_center(mouse_input, dimensions);
      } else if ci.is_colliding {
        self.stance = Stance::Still;
      } else {
        self.stance = Stance::Walking;
        self.orientation = ci.orientation;
      }
    }
  }
}

impl Default for CharacterDrawable {
  fn default() -> Self {
    CharacterDrawable::new()
  }
}

impl specs::prelude::Component for CharacterDrawable {
  type Storage = specs::storage::VecStorage<CharacterDrawable>;
}

pub struct CharacterDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> CharacterDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> CharacterDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let charter_bytes = &include_bytes!("../../assets/character.png")[..];

    let vertex_data: [VertexData; 4] = [
      VertexData::new([-20.0, -28.0], [0.0, 1.0]),
      VertexData::new([20.0, -28.0], [1.0, 1.0]),
      VertexData::new([20.0, 28.0], [1.0, 0.0]),
      VertexData::new([-20.0, 28.0], [0.0, 0.0]),
    ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &index_data[..]);

    let char_texture = load_texture(factory, charter_bytes);
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_character();

    CharacterDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, character_idx: usize, character_fire_idx: usize, drawable: &mut CharacterDrawable) -> CharacterSheet {
    let char_sprite =
      if drawable.orientation == Orientation::Still && drawable.stance == Stance::Walking {
        let sprite_idx = (drawable.direction as usize * 28 + RUN_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      } else if drawable.stance == Stance::Walking {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 28 + character_idx + RUN_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      } else {
        let sprite_idx = (drawable.orientation as usize * 8 + character_fire_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      };

    let elements_x = CHARACTER_SHEET_TOTAL_WIDTH / (char_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div: 0.0,
      row_idx: 0,
      index: char_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut CharacterDrawable,
                 character: &CharacterSprite,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(character.character_idx,
                                                         character.character_fire_idx,
                                                         &mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::prelude::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, MouseInputState>,
                     ReadStorage<'a, Zombies>,
                     Read<'a, Dimensions>);

  fn run(&mut self, (mut character, camera_input, character_input, mouse_input, zombies, dim): Self::SystemData) {
    use specs::join::Join;

    for (c, camera, ci, mi, zs) in (&mut character, &camera_input, &character_input, &mouse_input, &zombies).join() {
      let world_to_clip = dim.world_to_projection(camera);
      c.update(&world_to_clip, ci, mi, &dim, &zs.zombies);
    }
  }
}
<commit_msg>Reduce character hit area<commit_after>use cgmath;
use character::controls::CharacterInputState;
use critter::{CharacterSprite, CritterData};
use data;
use game::constants::{ASPECT_RATIO, CHARACTER_SHEET_TOTAL_WIDTH, RUN_SPRITE_OFFSET, SPRITE_OFFSET};
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::mouse_controls::MouseInputState;
use graphics::{camera::CameraInputState, Dimensions, get_orientation_from_center, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::prelude::{Read, ReadStorage, WriteStorage};
use zombie::{ZombieDrawable, zombies::Zombies};

pub mod controls;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Clone)]
pub struct CharacterDrawable {
  projection: Projection,
  pub position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
}

impl CharacterDrawable {
  pub fn new() -> CharacterDrawable {
    let view = Dimensions::get_view_matrix();
    CharacterDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(cgmath::Deg(75.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position: Position::new([0.0, 0.0]),
      orientation: Orientation::Right,
      stance: Stance::Walking,
      direction: Orientation::Right,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, mouse_input: &MouseInputState, dimensions: &Dimensions, zombies: &Vec<ZombieDrawable>) {
    self.projection = *world_to_clip;

    if zombies.iter()
              .any(|z| self.stance != Stance::NormalDeath &&
                overlaps(Position::new([ci.x_movement, ci.y_movement]), z.position, 10.0, 10.0)) {
      self.stance = Stance::NormalDeath;
      println!("Player died, press Esc to quit");
    }

    if self.stance != Stance::NormalDeath {
      if ci.is_shooting && mouse_input.left_click_point.is_some() && !ci.is_colliding {
        self.stance = Stance::Firing;
        self.orientation = get_orientation_from_center(mouse_input, dimensions);
      } else if ci.is_colliding {
        self.stance = Stance::Still;
      } else {
        self.stance = Stance::Walking;
        self.orientation = ci.orientation;
      }
    }
  }
}

impl Default for CharacterDrawable {
  fn default() -> Self {
    CharacterDrawable::new()
  }
}

impl specs::prelude::Component for CharacterDrawable {
  type Storage = specs::storage::VecStorage<CharacterDrawable>;
}

pub struct CharacterDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> CharacterDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> CharacterDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let charter_bytes = &include_bytes!("../../assets/character.png")[..];

    let vertex_data: [VertexData; 4] = [
      VertexData::new([-20.0, -28.0], [0.0, 1.0]),
      VertexData::new([20.0, -28.0], [1.0, 1.0]),
      VertexData::new([20.0, 28.0], [1.0, 0.0]),
      VertexData::new([-20.0, 28.0], [0.0, 0.0]),
    ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &index_data[..]);

    let char_texture = load_texture(factory, charter_bytes);
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_character();

    CharacterDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, character_idx: usize, character_fire_idx: usize, drawable: &mut CharacterDrawable) -> CharacterSheet {
    let char_sprite =
      if drawable.orientation == Orientation::Still && drawable.stance == Stance::Walking {
        let sprite_idx = (drawable.direction as usize * 28 + RUN_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      } else if drawable.stance == Stance::Walking {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 28 + character_idx + RUN_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      } else {
        let sprite_idx = (drawable.orientation as usize * 8 + character_fire_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      };

    let elements_x = CHARACTER_SHEET_TOTAL_WIDTH / (char_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div: 0.0,
      row_idx: 0,
      index: char_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut CharacterDrawable,
                 character: &CharacterSprite,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(character.character_idx,
                                                         character.character_fire_idx,
                                                         &mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::prelude::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, MouseInputState>,
                     ReadStorage<'a, Zombies>,
                     Read<'a, Dimensions>);

  fn run(&mut self, (mut character, camera_input, character_input, mouse_input, zombies, dim): Self::SystemData) {
    use specs::join::Join;

    for (c, camera, ci, mi, zs) in (&mut character, &camera_input, &character_input, &mouse_input, &zombies).join() {
      let world_to_clip = dim.world_to_projection(camera);
      c.update(&world_to_clip, ci, mi, &dim, &zs.zombies);
    }
  }
}
<|endoftext|>