Add selectable Pixel clock styling and widget font controls
This commit is contained in:
1 parent
d71fab7102
commit
dcca5fec88
14 files changed
+479
-37
No files matched your search
Binary file not shown.
@@ -0,0 +1,111 @@
|
|||||||
|
Copyright (c) 2009--2019, The Cantarell Authors
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Scripts contain parts of the source code of glyphsLib:
|
||||||
|
|
||||||
|
# Copyright 2015 Google Inc. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
Copyright 2011 The Comfortaa Project Authors
|
||||||
|
(https://github.com/alexeiva/comfortaa), with Reserved Font Name "Comfortaa".
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
Binary file not shown.
@@ -179,9 +179,18 @@ pub enum Alignment {
|
|||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ClockStyle {
|
||||||
|
#[default]
|
||||||
|
Pixel,
|
||||||
|
Minimal,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||||
#[serde(default, deny_unknown_fields)]
|
#[serde(default, deny_unknown_fields)]
|
||||||
pub struct Clock {
|
pub struct Clock {
|
||||||
|
pub style: ClockStyle,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
/// Empty means every output; otherwise exact connector names.
|
/// Empty means every output; otherwise exact connector names.
|
||||||
pub outputs: Vec<String>,
|
pub outputs: Vec<String>,
|
||||||
@@ -212,19 +221,20 @@ pub struct Clock {
|
|||||||
impl Default for Clock {
|
impl Default for Clock {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
style: ClockStyle::Pixel,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
outputs: Vec::new(),
|
outputs: Vec::new(),
|
||||||
anchor: Anchor::TopLeft,
|
anchor: Anchor::TopLeft,
|
||||||
margin_x: 64,
|
margin_x: 64,
|
||||||
margin_y: 80,
|
margin_y: 80,
|
||||||
width: 440,
|
width: 440,
|
||||||
height: 160,
|
height: 320,
|
||||||
font_family: None,
|
font_family: None,
|
||||||
bold: false,
|
bold: false,
|
||||||
time_size: 72.,
|
time_size: 128.,
|
||||||
date_size: 18.,
|
date_size: 18.,
|
||||||
time_format: "%-I:%M %p".into(),
|
time_format: "%-I:%M %p".into(),
|
||||||
date_format: "%A, %-d %B".into(),
|
date_format: "%a, %b %-d".into(),
|
||||||
time_zone: None,
|
time_zone: None,
|
||||||
show_date: true,
|
show_date: true,
|
||||||
lowercase: true,
|
lowercase: true,
|
||||||
|
|||||||
@@ -14,15 +14,16 @@ Enable it in Settings → Desktop widgets or in your Ferese config:
|
|||||||
```kdl
|
```kdl
|
||||||
desktop-widgets {
|
desktop-widgets {
|
||||||
clock enabled=#true anchor="top_left" margin-x=64 margin-y=80 {
|
clock enabled=#true anchor="top_left" margin-x=64 margin-y=80 {
|
||||||
|
style "pixel" // or "minimal"
|
||||||
outputs // every output; or outputs "DP-2"
|
outputs // every output; or outputs "DP-2"
|
||||||
width 440
|
width 440
|
||||||
height 160
|
height 320
|
||||||
font-family "" // inherit the desktop font
|
font-family "" // style default; or "Comfortaa"
|
||||||
bold #false
|
bold #false
|
||||||
time-size 72.0
|
time-size 128.0
|
||||||
date-size 18.0
|
date-size 18.0
|
||||||
time-format "%-I:%M %p" // use "%H:%M" for 24-hour time
|
time-format "%-I:%M %p" // use "%H:%M" for 24-hour time
|
||||||
date-format "%A, %-d %B"
|
date-format "%a, %b %-d"
|
||||||
time-zone "" // system zone; or "Africa/Lagos"
|
time-zone "" // system zone; or "Africa/Lagos"
|
||||||
show-date #true
|
show-date #true
|
||||||
lowercase #true
|
lowercase #true
|
||||||
@@ -45,6 +46,8 @@ Margins affect anchored axes only: a centered horizontal position ignores
|
|||||||
All geometry and text sizes use logical pixels and follow the output's scale.
|
All geometry and text sizes use logical pixels and follow the output's scale.
|
||||||
Increase the widget width/height for longer formats or larger text.
|
Increase the widget width/height for longer formats or larger text.
|
||||||
|
|
||||||
|
Pixel is the default clock style: heavy hours above minutes, alternating theme colors, and a small plain date above. Its bundled Cantarell display font can be replaced with the font selector. Minimal retains the plain time/date layout. Pixel zero-pads hours and hides am/pm for the stacked display. Formats with seconds or additional text retain their full formatted label on one line. Both styles shrink time text to fit smaller widget dimensions; an explicit text color overrides the alternating colors.
|
||||||
|
|
||||||
Saving reloads appearance and labels live. Placement, size, output filters and
|
Saving reloads appearance and labels live. Placement, size, output filters and
|
||||||
enable/disable changes recreate only clock surfaces; normal windows and the bar
|
enable/disable changes recreate only clock surfaces; normal windows and the bar
|
||||||
remain intact. Disconnecting an output destroys its clock; reconnecting creates
|
remain intact. Disconnecting an output destroys its clock; reconnecting creates
|
||||||
@@ -84,6 +87,8 @@ click Edit for its multiline editor, then Done to finish. Text autosaves after
|
|||||||
500 ms without typing. Escape cancels a drag or finishes editing. Position saves
|
500 ms without typing. Escape cancels a drag or finishes editing. Position saves
|
||||||
on release; dragging changes the anchor to `top_left` and saves pixel margins.
|
on release; dragging changes the anchor to `top_left` and saves pixel margins.
|
||||||
Title, size and style are also editable in Settings. Notes persist in the config.
|
Title, size and style are also editable in Settings. Notes persist in the config.
|
||||||
|
Choose each note’s font in Settings using the installed-font dropdown or enter a family name manually. Default font inherits the interface font. In KDL, use `font-family "Comfortaa"` inside the `note` node.
|
||||||
|
|
||||||
These are plain-text notes, not Markdown or HTML.
|
These are plain-text notes, not Markdown or HTML.
|
||||||
|
|
||||||
```kdl
|
```kdl
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ window-rule title="Choose a wallpaper" floating=#true
|
|||||||
|
|
||||||
desktop-widgets {
|
desktop-widgets {
|
||||||
clock {
|
clock {
|
||||||
|
style "pixel"
|
||||||
enabled #false
|
enabled #false
|
||||||
anchor "top_left"
|
anchor "top_left"
|
||||||
margin-x 64
|
margin-x 64
|
||||||
margin-y 80
|
margin-y 80
|
||||||
time-size 72.0
|
time-size 128.0
|
||||||
date-size 18.0
|
date-size 18.0
|
||||||
opacity 0.9
|
opacity 0.9
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,13 +48,13 @@ trap '[[ -z $input ]] || rm -f -- "$input"; [[ -s $file ]] || rm -f -- "$file"'
|
|||||||
input=$(mktemp "$XDG_RUNTIME_DIR/ferese-screenshot-XXXXXX.png") || fail 'Could not create the temporary capture.'
|
input=$(mktemp "$XDG_RUNTIME_DIR/ferese-screenshot-XXXXXX.png") || fail 'Could not create the temporary capture.'
|
||||||
grim "${capture_args[@]}" "$input" || fail 'Could not capture the screen. Log back into Ferese after enabling screen capture.'
|
grim "${capture_args[@]}" "$input" || fail 'Could not capture the screen. Log back into Ferese after enabling screen capture.'
|
||||||
# Enter saves the annotated image and copies it; Escape discards the capture.
|
# Enter saves the annotated image and copies it; Escape discards the capture.
|
||||||
# Do not pass the selector lock to Satty or its clipboard subprocess.
|
exec 9>&-
|
||||||
satty --filename "$input" --output-filename "$file" \
|
satty --filename "$input" --output-filename "$file" \
|
||||||
--fullscreen current-screen \
|
--fullscreen current-screen \
|
||||||
--copy-command 'wl-copy --type image/png' \
|
--copy-command 'wl-copy --type image/png' \
|
||||||
--save-after-copy \
|
--save-after-copy \
|
||||||
--actions-on-enter save-to-file,save-to-clipboard,exit \
|
--actions-on-enter save-to-file,save-to-clipboard,exit \
|
||||||
--actions-on-escape exit 9>&- || fail 'Could not open the screenshot in Satty.'
|
--actions-on-escape exit || fail 'Could not open the screenshot in Satty.'
|
||||||
if [[ -s $file ]]; then
|
if [[ -s $file ]]; then
|
||||||
printf '%s\n' "$file"
|
printf '%s\n' "$file"
|
||||||
fi
|
fi
|
||||||
@@ -59,6 +59,8 @@ install -m 0755 -- "$repo_dir/packaging/ferese-session" "$release_dir/ferese-ses
|
|||||||
install -m 0755 -- "$repo_dir/packaging/ferese-lock" "$release_dir/ferese-lock"
|
install -m 0755 -- "$repo_dir/packaging/ferese-lock" "$release_dir/ferese-lock"
|
||||||
install -m 0755 -- "$repo_dir/packaging/ferese-screenshot" "$release_dir/ferese-screenshot"
|
install -m 0755 -- "$repo_dir/packaging/ferese-screenshot" "$release_dir/ferese-screenshot"
|
||||||
install -m 0644 -- "$repo_dir/packaging/config.kdl" "$release_dir/config.example.kdl"
|
install -m 0644 -- "$repo_dir/packaging/config.kdl" "$release_dir/config.example.kdl"
|
||||||
|
install -D -m 0644 -- "$repo_dir/assets/fonts/Comfortaa-LICENSE.txt" "$release_dir/licenses/Comfortaa-LICENSE.txt"
|
||||||
|
install -D -m 0644 -- "$repo_dir/assets/fonts/Cantarell-LICENSE.txt" "$release_dir/licenses/Cantarell-LICENSE.txt"
|
||||||
install -d -m 0755 -- "$release_dir/wallpapers"
|
install -d -m 0755 -- "$release_dir/wallpapers"
|
||||||
install -m 0644 -- "$repo_dir/assets/wallpapers/ferese.png" "$release_dir/wallpapers/ferese.png"
|
install -m 0644 -- "$repo_dir/assets/wallpapers/ferese.png" "$release_dir/wallpapers/ferese.png"
|
||||||
install -m 0644 -- "$repo_dir/assets/wallpapers/ferese.svg" "$release_dir/wallpapers/ferese.svg"
|
install -m 0644 -- "$repo_dir/assets/wallpapers/ferese.svg" "$release_dir/wallpapers/ferese.svg"
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use std::{process::Command, sync::OnceLock};
|
||||||
|
|
||||||
|
pub fn families() -> &'static [String] {
|
||||||
|
static FAMILIES: OnceLock<Vec<String>> = OnceLock::new();
|
||||||
|
|
||||||
|
FAMILIES.get_or_init(|| {
|
||||||
|
let output = Command::new("fc-list")
|
||||||
|
.args(["--format", "%{family}\\n"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|output| output.status.success());
|
||||||
|
let mut families: Vec<String> = output
|
||||||
|
.as_ref()
|
||||||
|
.map(|output| String::from_utf8_lossy(&output.stdout))
|
||||||
|
.unwrap_or_default()
|
||||||
|
.lines()
|
||||||
|
.flat_map(|line| line.split(','))
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|family| !family.is_empty() && family.len() <= 128)
|
||||||
|
.map(str::to_owned)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
families.extend([
|
||||||
|
"Comfortaa".into(),
|
||||||
|
"sans-serif".into(),
|
||||||
|
"serif".into(),
|
||||||
|
"monospace".into(),
|
||||||
|
]);
|
||||||
|
families.sort_unstable_by_key(|family| family.to_lowercase());
|
||||||
|
families.dedup();
|
||||||
|
families.insert(0, "Default font".into());
|
||||||
|
families
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
mod fonts;
|
||||||
mod schema;
|
mod schema;
|
||||||
mod store;
|
mod store;
|
||||||
mod visuals;
|
mod visuals;
|
||||||
@@ -26,6 +27,7 @@ fn main() -> cosmic::iced::Result {
|
|||||||
None => store::config_path(),
|
None => store::config_path(),
|
||||||
};
|
};
|
||||||
let path = path.canonicalize().unwrap_or(path);
|
let path = path.canonicalize().unwrap_or(path);
|
||||||
|
fonts::families();
|
||||||
let initial = Snapshot::read(&path);
|
let initial = Snapshot::read(&path);
|
||||||
let theme = visuals::native_theme(initial.as_ref().ok());
|
let theme = visuals::native_theme(initial.as_ref().ok());
|
||||||
|
|
||||||
@@ -45,6 +47,7 @@ enum Message {
|
|||||||
Page(Page),
|
Page(Page),
|
||||||
Search(String),
|
Search(String),
|
||||||
Change(Edit),
|
Change(Edit),
|
||||||
|
SelectFont(String, String),
|
||||||
Draft(String, String),
|
Draft(String, String),
|
||||||
Commit(Field),
|
Commit(Field),
|
||||||
Range(Field, f64),
|
Range(Field, f64),
|
||||||
@@ -212,6 +215,10 @@ impl cosmic::Application for App {
|
|||||||
self.sync_notes();
|
self.sync_notes();
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
Message::SelectFont(path, family) => {
|
||||||
|
self.inputs.remove(&path);
|
||||||
|
return self.change(set(&path, family));
|
||||||
|
}
|
||||||
Message::DragWindow => return self.core.drag(None),
|
Message::DragWindow => return self.core.drag(None),
|
||||||
Message::ExternalConfig(result) => match result {
|
Message::ExternalConfig(result) => match result {
|
||||||
Ok(snapshot)
|
Ok(snapshot)
|
||||||
@@ -1085,6 +1092,41 @@ impl App {
|
|||||||
let labels = labels.width(Length::Fill);
|
let labels = labels.width(Length::Fill);
|
||||||
let path = field.path.clone();
|
let path = field.path.clone();
|
||||||
let control: Element<'static, Message> = match field.kind.clone() {
|
let control: Element<'static, Message> = match field.kind.clone() {
|
||||||
|
Kind::Font => {
|
||||||
|
let value = self
|
||||||
|
.inputs
|
||||||
|
.get(&path)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| self.draft.string(&path, ""));
|
||||||
|
let families = fonts::families();
|
||||||
|
let selected = if value.is_empty() {
|
||||||
|
Some(0)
|
||||||
|
} else {
|
||||||
|
families.iter().position(|family| family == &value)
|
||||||
|
};
|
||||||
|
let selection_path = path.clone();
|
||||||
|
let commit = field.clone();
|
||||||
|
column([])
|
||||||
|
.spacing(6)
|
||||||
|
.push(
|
||||||
|
widget::dropdown(families, selected, move |index| {
|
||||||
|
let family = if index == 0 { "" } else { &families[index] };
|
||||||
|
Message::SelectFont(selection_path.clone(), family.to_owned())
|
||||||
|
})
|
||||||
|
.width(225),
|
||||||
|
)
|
||||||
|
.push(
|
||||||
|
text_input("Default font", value)
|
||||||
|
.font(self.font)
|
||||||
|
.style(visuals::input_style(palette))
|
||||||
|
.on_input(move |value| Message::Draft(path.clone(), value))
|
||||||
|
.on_submit(move |_| Message::Commit(field.clone()))
|
||||||
|
.on_unfocus(Message::Commit(commit))
|
||||||
|
.width(225)
|
||||||
|
.size(12),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
Kind::Toggle(default) => toggler(self.draft.boolean(&path, default))
|
Kind::Toggle(default) => toggler(self.draft.boolean(&path, default))
|
||||||
.size(22)
|
.size(22)
|
||||||
.on_toggle(move |value| Message::Change(set(&path, value)))
|
.on_toggle(move |value| Message::Change(set(&path, value)))
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ impl Page {
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Kind {
|
pub enum Kind {
|
||||||
|
Font,
|
||||||
Toggle(bool),
|
Toggle(bool),
|
||||||
Range {
|
Range {
|
||||||
default: f64,
|
default: f64,
|
||||||
@@ -198,6 +199,13 @@ fn clock_fields() -> Vec<Field> {
|
|||||||
"Behind windows and click-through.",
|
"Behind windows and click-through.",
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
choice(
|
||||||
|
"desktop_widgets.clock.style",
|
||||||
|
"Clock style",
|
||||||
|
"Pixel stacks heavy, alternating-color digits; Minimal uses plain text.",
|
||||||
|
"pixel",
|
||||||
|
&[("pixel", "Pixel"), ("minimal", "Minimal")],
|
||||||
|
),
|
||||||
toggle(
|
toggle(
|
||||||
"desktop_widgets.clock.bold",
|
"desktop_widgets.clock.bold",
|
||||||
"Bold text",
|
"Bold text",
|
||||||
@@ -207,7 +215,7 @@ fn clock_fields() -> Vec<Field> {
|
|||||||
toggle(
|
toggle(
|
||||||
"desktop_widgets.clock.show_date",
|
"desktop_widgets.clock.show_date",
|
||||||
"Show date",
|
"Show date",
|
||||||
"A separate line beneath the time.",
|
"A small date above the digits in Pixel; beneath the time in Minimal.",
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
toggle(
|
toggle(
|
||||||
@@ -222,11 +230,11 @@ fn clock_fields() -> Vec<Field> {
|
|||||||
"top_left, top_center, top_right, center_left, center, center_right, bottom_left, bottom_center, bottom_right",
|
"top_left, top_center, top_right, center_left, center, center_right, bottom_left, bottom_center, bottom_right",
|
||||||
"top_left",
|
"top_left",
|
||||||
),
|
),
|
||||||
text(
|
Field::new(
|
||||||
"desktop_widgets.clock.font_family",
|
"desktop_widgets.clock.font_family",
|
||||||
"Font",
|
"Font",
|
||||||
"Empty follows the desktop font.",
|
"Choose an installed family or enter a name. Empty uses the style default.",
|
||||||
"",
|
Kind::Font,
|
||||||
),
|
),
|
||||||
text(
|
text(
|
||||||
"desktop_widgets.clock.time_format",
|
"desktop_widgets.clock.time_format",
|
||||||
@@ -238,7 +246,7 @@ fn clock_fields() -> Vec<Field> {
|
|||||||
"desktop_widgets.clock.date_format",
|
"desktop_widgets.clock.date_format",
|
||||||
"Date format",
|
"Date format",
|
||||||
"%A, %-d %B for weekday and date; %-d %b for a short date.",
|
"%A, %-d %B for weekday and date; %-d %b for a short date.",
|
||||||
"%A, %-d %B",
|
"%a, %b %-d",
|
||||||
),
|
),
|
||||||
text(
|
text(
|
||||||
"desktop_widgets.clock.time_zone",
|
"desktop_widgets.clock.time_zone",
|
||||||
@@ -301,7 +309,7 @@ fn clock_fields() -> Vec<Field> {
|
|||||||
"desktop_widgets.clock.height",
|
"desktop_widgets.clock.height",
|
||||||
"Widget height",
|
"Widget height",
|
||||||
"Leave room for both text lines.",
|
"Leave room for both text lines.",
|
||||||
160.0,
|
320.0,
|
||||||
32.0,
|
32.0,
|
||||||
800.0,
|
800.0,
|
||||||
1.0,
|
1.0,
|
||||||
@@ -312,7 +320,7 @@ fn clock_fields() -> Vec<Field> {
|
|||||||
"desktop_widgets.clock.time_size",
|
"desktop_widgets.clock.time_size",
|
||||||
"Time size",
|
"Time size",
|
||||||
"Logical pixels; scales with your output.",
|
"Logical pixels; scales with your output.",
|
||||||
72.0,
|
128.0,
|
||||||
8.0,
|
8.0,
|
||||||
240.0,
|
240.0,
|
||||||
1.0,
|
1.0,
|
||||||
@@ -411,11 +419,11 @@ pub fn note_fields(index: usize) -> Vec<Field> {
|
|||||||
"Nine positions, e.g. top_right or center.",
|
"Nine positions, e.g. top_right or center.",
|
||||||
"top_right",
|
"top_right",
|
||||||
),
|
),
|
||||||
text(
|
Field::new(
|
||||||
format!("{prefix}.font_family"),
|
format!("{prefix}.font_family"),
|
||||||
"Font",
|
"Font",
|
||||||
"Empty follows the desktop font.",
|
"Choose an installed family or enter a name. Empty follows the desktop font.",
|
||||||
"",
|
Kind::Font,
|
||||||
),
|
),
|
||||||
text(
|
text(
|
||||||
format!("{prefix}.color"),
|
format!("{prefix}.color"),
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ window-rule app-id="mine" floating=#true
|
|||||||
for field in fields {
|
for field in fields {
|
||||||
use crate::schema::Kind;
|
use crate::schema::Kind;
|
||||||
let values: Vec<Value> = match field.kind {
|
let values: Vec<Value> = match field.kind {
|
||||||
|
Kind::Font => vec!["".into(), "sans-serif".into()],
|
||||||
Kind::Toggle(_) => vec![true.into(), false.into()],
|
Kind::Toggle(_) => vec![true.into(), false.into()],
|
||||||
Kind::Range {
|
Kind::Range {
|
||||||
min, max, integer, ..
|
min, max, integer, ..
|
||||||
|
|||||||
+151
-17
@@ -107,6 +107,30 @@ impl From<ShellTheme> for BarMetrics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stacked_clock_digits(label: &str) -> Option<[String; 2]> {
|
||||||
|
let mut parts = label.split_whitespace();
|
||||||
|
let time = parts.next()?;
|
||||||
|
if let Some(period) = parts.next() {
|
||||||
|
if !period.eq_ignore_ascii_case("am") && !period.eq_ignore_ascii_case("pm") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if parts.next().is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (hour, minute) = time.split_once(':')?;
|
||||||
|
if !(1..=2).contains(&hour.len())
|
||||||
|
|| minute.len() != 2
|
||||||
|
|| !hour
|
||||||
|
.bytes()
|
||||||
|
.chain(minute.bytes())
|
||||||
|
.all(|digit| digit.is_ascii_digit())
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some([format!("{hour:0>2}"), minute.to_owned()])
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> cosmic::iced::Result {
|
fn main() -> cosmic::iced::Result {
|
||||||
// Small shell surfaces do not need a second GPU device and shader setup.
|
// Small shell surfaces do not need a second GPU device and shader setup.
|
||||||
// Keep wgpu as a fallback and honor an explicit renderer preference.
|
// Keep wgpu as a fallback and honor an explicit renderer preference.
|
||||||
@@ -115,6 +139,18 @@ fn main() -> cosmic::iced::Result {
|
|||||||
// toolkit threads are started.
|
// toolkit threads are started.
|
||||||
unsafe { std::env::set_var("ICED_BACKEND", "tiny-skia,wgpu") };
|
unsafe { std::env::set_var("ICED_BACKEND", "tiny-skia,wgpu") };
|
||||||
}
|
}
|
||||||
|
cosmic::iced::advanced::graphics::text::font_system()
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.load_font(std::borrow::Cow::Borrowed(include_bytes!(
|
||||||
|
"../../../assets/fonts/Comfortaa-Regular.otf"
|
||||||
|
)));
|
||||||
|
cosmic::iced::advanced::graphics::text::font_system()
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.load_font(std::borrow::Cow::Borrowed(include_bytes!(
|
||||||
|
"../../../assets/fonts/Cantarell-ExtraBold.otf"
|
||||||
|
)));
|
||||||
let mut config = config::load();
|
let mut config = config::load();
|
||||||
motion::configure(config.animations);
|
motion::configure(config.animations);
|
||||||
let compositor_wallpaper = std::env::var_os("FERESE_COMPOSITOR_WALLPAPER").is_some();
|
let compositor_wallpaper = std::env::var_os("FERESE_COMPOSITOR_WALLPAPER").is_some();
|
||||||
@@ -1336,16 +1372,22 @@ impl FereseShell {
|
|||||||
ClockAlignment::Center => alignment::Horizontal::Center,
|
ClockAlignment::Center => alignment::Horizontal::Center,
|
||||||
ClockAlignment::Right => alignment::Horizontal::Right,
|
ClockAlignment::Right => alignment::Horizontal::Right,
|
||||||
};
|
};
|
||||||
let mut font = configured_font(
|
let pixel = clock.style == ferese_core::desktop::ClockStyle::Pixel;
|
||||||
clock
|
let custom_font = clock
|
||||||
.font_family
|
.font_family
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.filter(|family| !family.trim().is_empty())
|
.filter(|family| !family.trim().is_empty());
|
||||||
.or(self.config.font_family.as_deref()),
|
let mut font = configured_font(if pixel {
|
||||||
);
|
custom_font.or(Some("Cantarell"))
|
||||||
if clock.bold {
|
} else {
|
||||||
|
custom_font.or(self.config.font_family.as_deref())
|
||||||
|
});
|
||||||
|
if pixel && custom_font.is_none() {
|
||||||
|
font.weight = cosmic::iced::font::Weight::Black;
|
||||||
|
} else if clock.bold {
|
||||||
font.weight = cosmic::iced::font::Weight::Bold;
|
font.weight = cosmic::iced::font::Weight::Bold;
|
||||||
}
|
}
|
||||||
|
let date_font = configured_font(custom_font.or(self.config.font_family.as_deref()));
|
||||||
let tint = |custom: &Option<String>, fallback| {
|
let tint = |custom: &Option<String>, fallback| {
|
||||||
let mut tint = color(
|
let mut tint = color(
|
||||||
custom
|
custom
|
||||||
@@ -1356,21 +1398,98 @@ impl FereseShell {
|
|||||||
tint.a *= clock.opacity;
|
tint.a *= clock.opacity;
|
||||||
tint
|
tint
|
||||||
};
|
};
|
||||||
|
let foreground = tint(&clock.color, self.config.theme.text_primary);
|
||||||
|
let accent = color(self.config.theme.accent);
|
||||||
|
let custom_color = clock
|
||||||
|
.color
|
||||||
|
.as_deref()
|
||||||
|
.and_then(config::parse_color)
|
||||||
|
.is_some();
|
||||||
|
let digit_colors = [0.8, 0.45].map(|text_mix| {
|
||||||
|
if custom_color {
|
||||||
|
return foreground;
|
||||||
|
}
|
||||||
|
Color {
|
||||||
|
r: foreground.r * text_mix + accent.r * (1.0 - text_mix),
|
||||||
|
g: foreground.g * text_mix + accent.g * (1.0 - text_mix),
|
||||||
|
b: foreground.b * text_mix + accent.b * (1.0 - text_mix),
|
||||||
|
a: foreground.a,
|
||||||
|
}
|
||||||
|
});
|
||||||
let mut labels = cosmic::widget::column([])
|
let mut labels = cosmic::widget::column([])
|
||||||
.spacing(clock.gap)
|
.spacing(clock.gap)
|
||||||
.align_x(alignment)
|
.align_x(alignment);
|
||||||
.push(
|
|
||||||
cosmic::widget::text(self.desktop_clock.0.clone())
|
if pixel && clock.show_date {
|
||||||
.font(font)
|
labels = labels.push(
|
||||||
.size(clock.time_size)
|
cosmic::widget::text(self.desktop_clock.1.clone())
|
||||||
.width(Length::Fill)
|
.font(date_font)
|
||||||
.align_x(alignment)
|
.size(clock.date_size)
|
||||||
.class(theme::Text::Color(tint(
|
.class(theme::Text::Color(tint(
|
||||||
&clock.color,
|
&clock.date_color,
|
||||||
self.config.theme.text_primary,
|
self.config.theme.text_primary,
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
if clock.show_date {
|
}
|
||||||
|
|
||||||
|
if let Some(digits) = pixel
|
||||||
|
.then(|| stacked_clock_digits(&self.desktop_clock.0))
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
|
let available_height = (clock.height as f32
|
||||||
|
- clock.padding * 2.0
|
||||||
|
- if clock.show_date {
|
||||||
|
clock.date_size * 1.3 + clock.gap
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
})
|
||||||
|
.max(16.0);
|
||||||
|
let size = clock
|
||||||
|
.time_size
|
||||||
|
.min(available_height / 1.6)
|
||||||
|
.min((clock.width as f32 - clock.padding * 2.0).max(16.0) / 1.5);
|
||||||
|
let mut stack = cosmic::widget::column([]).spacing(0).align_x(alignment);
|
||||||
|
|
||||||
|
for (line, digits) in digits.into_iter().enumerate() {
|
||||||
|
let mut digit_row = row([]).spacing(0);
|
||||||
|
for (position, digit) in digits.chars().enumerate() {
|
||||||
|
digit_row = digit_row.push(
|
||||||
|
cosmic::widget::text(digit.to_string())
|
||||||
|
.font(font)
|
||||||
|
.size(size)
|
||||||
|
.line_height(cosmic::iced::widget::text::LineHeight::Relative(0.8))
|
||||||
|
.class(theme::Text::Color(digit_colors[(line + position) % 2])),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
stack = stack.push(digit_row);
|
||||||
|
}
|
||||||
|
labels = labels.push(stack);
|
||||||
|
} else {
|
||||||
|
let characters = self.desktop_clock.0.chars().count().max(1) as f32;
|
||||||
|
let available_width = (clock.width as f32 - clock.padding * 2.0).max(1.0);
|
||||||
|
let available_height = (clock.height as f32
|
||||||
|
- clock.padding * 2.0
|
||||||
|
- if clock.show_date {
|
||||||
|
clock.date_size * 1.3 + clock.gap
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
})
|
||||||
|
.max(1.0);
|
||||||
|
let size = clock
|
||||||
|
.time_size
|
||||||
|
.min(available_width / (characters * 0.75))
|
||||||
|
.min(available_height / 1.3);
|
||||||
|
labels = labels.push(
|
||||||
|
cosmic::widget::text(self.desktop_clock.0.clone())
|
||||||
|
.font(font)
|
||||||
|
.size(size)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.align_x(alignment)
|
||||||
|
.class(theme::Text::Color(foreground)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pixel && clock.show_date {
|
||||||
labels = labels.push(
|
labels = labels.push(
|
||||||
cosmic::widget::text(self.desktop_clock.1.clone())
|
cosmic::widget::text(self.desktop_clock.1.clone())
|
||||||
.font(font)
|
.font(font)
|
||||||
@@ -2381,6 +2500,21 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pixel_clock_stacks_only_hour_and_minute_formats() {
|
||||||
|
assert_eq!(
|
||||||
|
super::stacked_clock_digits("2:45 pm"),
|
||||||
|
Some(["02".into(), "45".into()])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
super::stacked_clock_digits("14:45"),
|
||||||
|
Some(["14".into(), "45".into()])
|
||||||
|
);
|
||||||
|
assert!(super::stacked_clock_digits("14:45:30").is_none());
|
||||||
|
assert!(super::stacked_clock_digits("Today 14:45").is_none());
|
||||||
|
assert!(super::stacked_clock_digits("14:45 pm extra").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn note_drag_origin_and_bounds_use_logical_output_coordinates() {
|
fn note_drag_origin_and_bounds_use_logical_output_coordinates() {
|
||||||
use cosmic::iced::Point;
|
use cosmic::iced::Point;
|
||||||
|
|||||||
Reference in new issue
Block a user