~julienxx/castor

67a3ae1c4023b658f99d6bc82136002c07f389b6 — Julien Blanchard a month ago 19bcd04 0.8.9
Font are customizable

- Can change family (sans/serif/monospace)
- Can change size
- Can change style (normal/italic/oblique)
M Cargo.lock => Cargo.lock +2 -1
@@ 112,7 112,7 @@ dependencies = [

[[package]]
name = "castor"
version = "0.8.8"
version = "0.8.9"
dependencies = [
 "ansi-parser",
 "dirs",


@@ 127,6 127,7 @@ dependencies = [
 "open",
 "openssl",
 "pango",
 "pango-sys",
 "percent-encoding",
 "regex",
 "serde",

M Cargo.toml => Cargo.toml +2 -1
@@ 1,6 1,6 @@
[package]
name = "castor"
version = "0.8.8"
version = "0.8.9"
authors = ["Julien Blanchard <julien@typed-hole.org>"]
edition = "2018"



@@ 13,6 13,7 @@ gio = "*"
glib = "*"
glib-sys = "*"
pango = "*"
pango-sys = "*"
open = "*"
regex = "*"
native-tls = "*"

M README.md => README.md +27 -6
@@ 82,12 82,33 @@ background = "#FFC0CB"
h1 = ">"
h2 = "))"
h3 = "}}}"
list = "🌼"

[monospace]
gemini=false
gopher=true
finger=true
list = "🌼 "

[fonts]
[fonts.finger]
    family = "monospace"
    size = 11
[fonts.gemini]
    family = "serif"
    size = 11
[fonts.gemini.h1]
    family = "sans"
    size = 16
    style = "normal"
[fonts.gemini.h2]
    family = "monospace"
    size = 13
    style = "italic"
[fonts.gemini.h3]
    family = "monospace"
    size = 12
[fonts.gemini.list]
    family = "sans"
    size = 11
    style = "oblique"
[fonts.gopher]
    family = "monospace"
    size = 11
```



M data/castor_settings.toml.example => data/castor_settings.toml.example +26 -5
@@ 13,9 13,30 @@ background = "#FFC0CB"
h1 = ">"
h2 = "))"
h3 = "}}}"
list = "🌼"
list = "🌼 "

[monospace]
gemini=false
gopher=true
finger=true
[fonts]
[fonts.finger]
    family = "monospace"
    size = 11
[fonts.gemini]
    family = "serif"
    size = 11
[fonts.gemini.h1]
    family = "sans"
    size = 16
    style = "normal"
[fonts.gemini.h2]
    family = "monospace"
    size = 13
    style = "italic"
[fonts.gemini.h3]
    family = "monospace"
    size = 12
[fonts.gemini.list]
    family = "sans"
    size = 11
    style = "oblique"
[fonts.gopher]
    family = "monospace"
    size = 11

M src/draw.rs => src/draw.rs +40 -42
@@ 19,11 19,7 @@ pub fn gemini_content(
    let buffer = content_view.get_buffer().unwrap();

    let mut mono_toggle = false;
    let font_family = if crate::settings::gemini_monospace() {
        "monospace"
    } else {
        "sans"
    };
    let font_family = crate::settings::get_gemini_text_font_family();

    for el in content {
        match el {


@@ 32,10 28,12 @@ pub fn gemini_content(
                buffer.insert_markup(
                    &mut end_iter,
                    &format!(
                        "<span foreground=\"{}\" size=\"x-large\" font_family=\"{}\">{}{}</span>\n",
                        crate::settings::h1_color(),
                        font_family,
                        crate::settings::h1_character(),
                        "<span foreground=\"{}\" size=\"{}\" font_family=\"{}\" style=\"{}\">{}{}</span>\n",
                        crate::settings::get_h1_color(),
                        crate::settings::get_gemini_h1_font_size(),
                        crate::settings::get_gemini_h1_font_family(),
                        crate::settings::get_gemini_h1_font_style(),
                        crate::settings::get_h1_character(),
                        escape_text(&header)
                    ),
                );


@@ 45,10 43,12 @@ pub fn gemini_content(
                buffer.insert_markup(
                    &mut end_iter,
                    &format!(
                        "<span foreground=\"{}\" size=\"large\" font_family=\"{}\">{}{}</span>\n",
                        crate::settings::h2_color(),
                        font_family,
                        crate::settings::h2_character(),
                        "<span foreground=\"{}\" size=\"{}\" font_family=\"{}\" style=\"{}\">{}{}</span>\n",
                        crate::settings::get_h2_color(),
                        crate::settings::get_gemini_h2_font_size(),
                        crate::settings::get_gemini_h2_font_family(),
                        crate::settings::get_gemini_h2_font_style(),
                        crate::settings::get_h2_character(),
                        escape_text(&header)
                    ),
                );


@@ 58,10 58,12 @@ pub fn gemini_content(
                buffer.insert_markup(
                    &mut end_iter,
                    &format!(
                        "<span foreground=\"{}\" size=\"medium\" font_family=\"{}\">{}{}</span>\n",
                        crate::settings::h3_color(),
                        font_family,
                        crate::settings::h3_character(),
                        "<span foreground=\"{}\" size=\"{}\" font_family=\"{}\" style=\"{}\">{}{}</span>\n",
                        crate::settings::get_h3_color(),
                        crate::settings::get_gemini_h3_font_size(),
                        crate::settings::get_gemini_h3_font_family(),
                        crate::settings::get_gemini_h3_font_style(),
                        crate::settings::get_h3_character(),
                        escape_text(&header)
                    ),
                );


@@ 71,10 73,12 @@ pub fn gemini_content(
                buffer.insert_markup(
                    &mut end_iter,
                    &format!(
                        "<span foreground=\"{}\" font_family=\"{}\">{} {}</span>\n",
                        crate::settings::list_color(),
                        font_family,
                        crate::settings::list_character(),
                        "<span foreground=\"{}\" size=\"{}\" font_family=\"{}\" style=\"{}\">{}{}</span>\n",
                        crate::settings::get_list_color(),
                        crate::settings::get_gemini_list_font_size(),
                        crate::settings::get_gemini_list_font_family(),
                        crate::settings::get_gemini_list_font_style(),
                        crate::settings::get_list_character(),
                        escape_text(&item)
                    ),
                );


@@ 94,8 98,9 @@ pub fn gemini_content(
                    buffer.insert_markup(
                        &mut end_iter,
                        &format!(
                            "<span foreground=\"{}\" font_family=\"monospace\">{}</span>\n",
                            crate::settings::text_color(),
                            "<span foreground=\"{}\" font_family=\"monospace\" size=\"{}\">{}</span>\n",
                            crate::settings::get_text_color(),
                            crate::settings::get_gemini_text_font_size(),
                            text
                        ),
                    );


@@ 103,9 108,10 @@ pub fn gemini_content(
                    buffer.insert_markup(
                        &mut end_iter,
                        &format!(
                            "<span foreground=\"{}\" font_family=\"{}\">{}</span>\n",
                            crate::settings::text_color(),
                            "<span foreground=\"{}\" font_family=\"{}\" size=\"{}\">{}</span>\n",
                            crate::settings::get_text_color(),
                            font_family,
                            crate::settings::get_gemini_text_font_size(),
                            text
                        ),
                    );


@@ 130,7 136,7 @@ pub fn gemini_text_content(gui: &Arc<Gui>, content: std::str::Lines) -> TextBuff
            &mut end_iter,
            &format!(
                "<span foreground=\"{}\" font_family=\"monospace\">{}</span>\n",
                crate::settings::text_color(),
                crate::settings::get_text_color(),
                escape_text(&line)
            ),
        );


@@ 149,11 155,6 @@ pub fn gopher_content(
        match el {
            Ok(crate::gopher::parser::TextElement::Text(text)) => {
                let mut end_iter = buffer.get_end_iter();
                let font_family = if crate::settings::gopher_monospace() {
                    "font_family=\"monospace\""
                } else {
                    "font_family=\"serif\""
                };

                let text = if text.contains("<span") {
                    text


@@ 164,9 165,10 @@ pub fn gopher_content(
                buffer.insert_markup(
                    &mut end_iter,
                    &format!(
                        "<span foreground=\"{}\" {}>{}</span>\n",
                        crate::settings::text_color(),
                        font_family,
                        "<span foreground=\"{}\" font_family=\"{}\" size=\"{}\">{}</span>\n",
                        crate::settings::get_text_color(),
                        crate::settings::get_gopher_font_family(),
                        crate::settings::get_gopher_font_size(),
                        text
                    ),
                );


@@ 200,18 202,14 @@ pub fn finger_content(
        match el {
            Ok(crate::finger::parser::TextElement::Text(text)) => {
                let mut end_iter = buffer.get_end_iter();
                let font_family = if crate::settings::finger_monospace() {
                    "font_family=\"monospace\""
                } else {
                    "font_family=\"serif\""
                };

                buffer.insert_markup(
                    &mut end_iter,
                    &format!(
                        "<span foreground=\"{}\" {}>{}</span>\n",
                        crate::settings::text_color(),
                        font_family,
                        "<span foreground=\"{}\" font_family=\"{}\" size=\"{}\">{}</span>\n",
                        crate::settings::get_text_color(),
                        crate::settings::get_finger_font_family(),
                        crate::settings::get_finger_font_size(),
                        escape_text(&text)
                    ),
                );

M src/finger/client.rs => src/finger/client.rs +3 -3
@@ 37,13 37,13 @@ pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), Strin

                        Ok((None, res))
                    })
                        .join()
                        .unwrap(),
                    .join()
                    .unwrap(),
                    Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)),
                }
            }
            None => Err(format!("Could not connect to {}\n", urlf)),
        }
        },
        Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)),
    }
}

M src/gemini/client.rs => src/gemini/client.rs +2 -2
@@ 31,8 31,8 @@ pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), Strin
                    V4(ip) => V4(ip),
                    V6(ip) => match addrs_iter.next() {
                        Some(addr) => addr,
                        None => V6(ip)
                    }
                        None => V6(ip),
                    },
                };

                let stream = TcpStream::connect_timeout(&socket_addr, Duration::new(5, 0));

M src/gopher/link.rs => src/gopher/link.rs +8 -0
@@ 170,6 170,14 @@ impl FromStr for Link {
            } else {
                Err(ParseError)
            }
        // } else if line.contains("://") {
        //     let url = extract_url(line);
        //     let label = String::from(line);

        //     match make_link(String::from(url), label) {
        //         Some(link) => Ok(link),
        //         None => Err(ParseError),
        //     }
        } else {
            Err(ParseError)
        }

M src/gopher/parser.rs => src/gopher/parser.rs +6 -1
@@ 42,7 42,12 @@ impl FromStr for TextElement {
            // Text line
            if line.contains("gopher://") {
                Ok(TextElement::LinkItem(String::from(line)))
            } else if line.contains("http://") || line.contains("https://") || line.contains("gemini://") || line.contains("finger://") || line.contains("ftp://") {
            } else if line.contains("http://")
                || line.contains("https://")
                || line.contains("gemini://")
                || line.contains("finger://")
                || line.contains("ftp://")
            {
                Ok(TextElement::ExternalLinkItem(String::from(line)))
            } else {
                Ok(TextElement::Text(colors::colorize(line)))

M src/main.rs => src/main.rs +3 -1
@@ 190,7 190,9 @@ pub fn visit_url<T: AbsoluteUrl + Protocol>(gui: &Arc<Gui>, url: T) {
            let absolute_url = url.to_absolute_url();

            match absolute_url {
                Ok(absolute_url) => match gemini::client::get_data(Gemini { source: absolute_url.to_string() }) {
                Ok(absolute_url) => match gemini::client::get_data(Gemini {
                    source: absolute_url.to_string(),
                }) {
                    Ok((meta, new_content)) => {
                        let meta_str = String::from_utf8_lossy(&meta.unwrap()).to_string();


M src/settings.rs => src/settings.rs +287 -90
@@ 12,7 12,7 @@ struct Settings {
    general: Option<General>,
    colors: Option<Color>,
    characters: Option<Character>,
    monospace: Option<Monospace>,
    fonts: Option<Font>,
}

#[derive(Deserialize)]


@@ 38,11 38,27 @@ struct Character {
    list: Option<String>,
}

#[derive(Deserialize)]
struct Monospace {
    finger: Option<bool>,
    gemini: Option<bool>,
    gopher: Option<bool>,
#[derive(Debug, Deserialize)]
struct Font {
    finger: Option<FontAttr>,
    gemini: Option<GeminiFontAttr>,
    gopher: Option<FontAttr>,
}

#[derive(Debug, Deserialize)]
struct FontAttr {
    family: Option<String>,
    style: Option<String>,
    size: Option<i32>,
}

#[derive(Debug, Deserialize)]
struct GeminiFontAttr {
    text: Option<FontAttr>,
    h1: Option<FontAttr>,
    h2: Option<FontAttr>,
    h3: Option<FontAttr>,
    list: Option<FontAttr>,
}

pub fn start_url() -> Option<String> {


@@ 52,130 68,311 @@ pub fn start_url() -> Option<String> {
    }
}

pub fn h1_color() -> String {
    match read().colors {
        Some(colors) => match colors.h1 {
            Some(color) => color,
            None => String::from("#9932CC"),
        },
const DEFAULT_FONT: &str = "serif";
const DEFAULT_FONT_STYLE: &str = "normal";
const DEFAULT_FONT_SIZE: i32 = 11 * pango_sys::PANGO_SCALE;
const DEFAULT_H1_FONT_SIZE: i32 = 16 * pango_sys::PANGO_SCALE;
const DEFAULT_H2_FONT_SIZE: i32 = 13 * pango_sys::PANGO_SCALE;
const DEFAULT_H3_FONT_SIZE: i32 = 12 * pango_sys::PANGO_SCALE;

fn finger_font_family() -> Option<String> {
    read().fonts?.finger?.family
}

fn finger_font_size() -> Option<i32> {
    read().fonts?.finger?.size.or(Some(DEFAULT_FONT_SIZE))
}

pub fn get_finger_font_family() -> String {
    match finger_font_family() {
        Some(family) => family,
        None => String::from(DEFAULT_FONT),
    }
}

pub fn get_finger_font_size() -> i32 {
    match finger_font_size() {
        Some(size) => size * pango_sys::PANGO_SCALE,
        None => DEFAULT_FONT_SIZE,
    }
}

fn gemini_text_font_family() -> Option<String> {
    read().fonts?.gemini?.text?.family
}

pub fn get_gemini_text_font_family() -> String {
    match gemini_text_font_family() {
        Some(family) => family,
        None => String::from(DEFAULT_FONT),
    }
}

fn gemini_text_font_size() -> Option<i32> {
    read().fonts?.gemini?.text?.size
}

pub fn get_gemini_text_font_size() -> i32 {
    match gemini_text_font_size() {
        Some(size) => size * pango_sys::PANGO_SCALE,
        None => DEFAULT_FONT_SIZE,
    }
}

fn gemini_h1_font_family() -> Option<String> {
    read().fonts?.gemini?.h1?.family
}

fn gemini_h1_font_size() -> Option<i32> {
    read().fonts?.gemini?.h1?.size
}

fn gemini_h1_font_style() -> Option<String> {
    read().fonts?.gemini?.h1?.style
}

pub fn get_gemini_h1_font_size() -> i32 {
    match gemini_h1_font_size() {
        Some(size) => size * pango_sys::PANGO_SCALE,
        None => DEFAULT_H1_FONT_SIZE,
    }
}

pub fn get_gemini_h1_font_family() -> String {
    match gemini_h1_font_family() {
        Some(family) => family,
        None => String::from(DEFAULT_FONT),
    }
}

pub fn get_gemini_h1_font_style() -> String {
    match gemini_h1_font_style() {
        Some(style) => style,
        None => String::from(DEFAULT_FONT_STYLE),
    }
}

fn gemini_h2_font_family() -> Option<String> {
    read().fonts?.gemini?.h2?.family
}

fn gemini_h2_font_size() -> Option<i32> {
    read().fonts?.gemini?.h2?.size
}

fn gemini_h2_font_style() -> Option<String> {
    read().fonts?.gemini?.h2?.style
}

pub fn get_gemini_h2_font_size() -> i32 {
    match gemini_h2_font_size() {
        Some(size) => size * pango_sys::PANGO_SCALE,
        None => DEFAULT_H2_FONT_SIZE,
    }
}

pub fn get_gemini_h2_font_family() -> String {
    match gemini_h2_font_family() {
        Some(family) => family,
        None => String::from(DEFAULT_FONT),
    }
}

pub fn get_gemini_h2_font_style() -> String {
    match gemini_h2_font_style() {
        Some(style) => style,
        None => String::from(DEFAULT_FONT_STYLE),
    }
}

fn gemini_h3_font_family() -> Option<String> {
    read().fonts?.gemini?.h3?.family
}

fn gemini_h3_font_size() -> Option<i32> {
    read().fonts?.gemini?.h3?.size
}

fn gemini_h3_font_style() -> Option<String> {
    read().fonts?.gemini?.h3?.style
}

pub fn get_gemini_h3_font_size() -> i32 {
    match gemini_h3_font_size() {
        Some(size) => size * pango_sys::PANGO_SCALE,
        None => DEFAULT_H3_FONT_SIZE,
    }
}

pub fn get_gemini_h3_font_family() -> String {
    match gemini_h3_font_family() {
        Some(family) => family,
        None => String::from(DEFAULT_FONT),
    }
}

pub fn get_gemini_h3_font_style() -> String {
    match gemini_h3_font_style() {
        Some(style) => style,
        None => String::from(DEFAULT_FONT_STYLE),
    }
}

fn gemini_list_font_family() -> Option<String> {
    read().fonts?.gemini?.list?.family
}

fn gemini_list_font_size() -> Option<i32> {
    read().fonts?.gemini?.list?.size
}

fn gemini_list_font_style() -> Option<String> {
    read().fonts?.gemini?.list?.style
}

pub fn get_gemini_list_font_size() -> i32 {
    match gemini_list_font_size() {
        Some(size) => size * pango_sys::PANGO_SCALE,
        None => DEFAULT_FONT_SIZE,
    }
}

pub fn get_gemini_list_font_family() -> String {
    match gemini_list_font_family() {
        Some(family) => family,
        None => String::from(DEFAULT_FONT),
    }
}

pub fn get_gemini_list_font_style() -> String {
    match gemini_list_font_style() {
        Some(style) => style,
        None => String::from(DEFAULT_FONT_STYLE),
    }
}

fn gopher_font_family() -> Option<String> {
    read().fonts?.gopher?.family
}

fn gopher_font_size() -> Option<i32> {
    read().fonts?.gopher?.size
}

pub fn get_gopher_font_family() -> String {
    match gopher_font_family() {
        Some(family) => family,
        None => String::from(DEFAULT_FONT),
    }
}

pub fn get_gopher_font_size() -> i32 {
    match gopher_font_size() {
        Some(size) => size * pango_sys::PANGO_SCALE,
        None => DEFAULT_FONT_SIZE,
    }
}

fn h1_color() -> Option<String> {
    read().colors?.h1
}

pub fn get_h1_color() -> String {
    match h1_color() {
        Some(color) => color,
        None => String::from("#9932CC"),
    }
}

pub fn h2_color() -> String {
    match read().colors {
        Some(colors) => match colors.h2 {
            Some(color) => color,
            None => String::from("#FF1493"),
        },
fn h2_color() -> Option<String> {
    read().colors?.h2
}

pub fn get_h2_color() -> String {
    match h2_color() {
        Some(color) => color,
        None => String::from("#FF1493"),
    }
}

pub fn h3_color() -> String {
    match read().colors {
        Some(colors) => match colors.h3 {
            Some(color) => color,
            None => String::from("#87CEFA"),
        },
fn h3_color() -> Option<String> {
    read().colors?.h3
}

pub fn get_h3_color() -> String {
    match h3_color() {
        Some(color) => color,
        None => String::from("#87CEFA"),
    }
}

pub fn list_color() -> String {
    match read().colors {
        Some(colors) => match colors.list {
            Some(color) => color,
            None => String::from("green"),
        },
fn list_color() -> Option<String> {
    read().colors?.list
}

pub fn get_list_color() -> String {
    match list_color() {
        Some(color) => color,
        None => String::from("green"),
    }
}

pub fn text_color() -> String {
    match read().colors {
        Some(colors) => match colors.text {
            Some(color) => color,
            None => String::from("black"),
        },
fn text_color() -> Option<String> {
    read().colors?.text
}

pub fn get_text_color() -> String {
    match text_color() {
        Some(color) => color,
        None => String::from("black"),
    }
}

pub fn background_color() -> Option<String> {
    match read().colors {
        Some(colors) => colors.background,
        None => None,
    }
    read().colors?.background
}

pub fn h1_character() -> String {
    match read().characters {
        Some(characters) => match characters.h1 {
            Some(character) => character,
            None => String::new(),
        },
        None => String::new(),
    }
fn h1_character() -> Option<String> {
    read().characters?.h1
}

pub fn h2_character() -> String {
    match read().characters {
        Some(characters) => match characters.h2 {
            Some(character) => character,
            None => String::new(),
        },
pub fn get_h1_character() -> String {
    match h1_character() {
        Some(char) => char,
        None => String::new(),
    }
}

pub fn h3_character() -> String {
    match read().characters {
        Some(characters) => match characters.h3 {
            Some(character) => character,
            None => String::new(),
        },
fn h2_character() -> Option<String> {
    read().characters?.h2
}

pub fn get_h2_character() -> String {
    match h2_character() {
        Some(char) => char,
        None => String::new(),
    }
}

pub fn list_character() -> String {
    match read().characters {
        Some(characters) => match characters.list {
            Some(character) => character,
            None => String::from("■"),
        },
        None => String::from("■"),
    }
fn h3_character() -> Option<String> {
    read().characters?.h3
}

pub fn finger_monospace() -> bool {
    match read().monospace {
        Some(monospace) => match monospace.finger {
            Some(setting) => setting,
            None => true,
        },
        None => true,
pub fn get_h3_character() -> String {
    match h3_character() {
        Some(char) => char,
        None => String::new(),
    }
}

pub fn gemini_monospace() -> bool {
    match read().monospace {
        Some(monospace) => match monospace.gemini {
            Some(setting) => setting,
            None => true,
        },
        None => true,
    }
fn list_character() -> Option<String> {
    read().characters?.list
}

pub fn gopher_monospace() -> bool {
    match read().monospace {
        Some(monospace) => match monospace.gopher {
            Some(setting) => setting,
            None => true,
        },
        None => true,
pub fn get_list_character() -> String {
    match list_character() {
        Some(char) => char,
        None => String::from("■"),
    }
}