Merge remote-tracking branch 'danielelmes/master' into fix_windows_build

# Conflicts:
#	Makefile
This commit is contained in:
evandrocoan 2020-02-25 03:16:57 -03:00
commit ac4933faca
17 changed files with 183 additions and 34 deletions

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
set -e set -eo pipefail
antispam=", at the domain " antispam=", at the domain "

View file

@ -79,7 +79,7 @@ build-qt:
.PHONY: clean .PHONY: clean
clean: clean-dist clean: clean-dist
set -e && \ set -eo pipefail && \
for dir in $(DEVEL); do \ for dir in $(DEVEL); do \
$(SUBMAKE) -C $$dir clean; \ $(SUBMAKE) -C $$dir clean; \
done done
@ -90,7 +90,7 @@ clean-dist:
.PHONY: check .PHONY: check
check: pyenv buildhash check: pyenv buildhash
set -e && \ set -eo pipefail && \
for dir in $(CHECKABLE_RS); do \ for dir in $(CHECKABLE_RS); do \
$(SUBMAKE) -C $$dir check; \ $(SUBMAKE) -C $$dir check; \
done; \ done; \
@ -116,3 +116,16 @@ add-buildhash:
ver=$$(cat meta/version); \ ver=$$(cat meta/version); \
hash=$$(cat meta/buildhash); \ hash=$$(cat meta/buildhash); \
rename "s/-$${ver}-/-$${ver}+$${hash}-/" dist/*-$$ver-* rename "s/-$${ver}-/-$${ver}+$${hash}-/" dist/*-$$ver-*
.PHONY: pull-i18n
pull-i18n:
(cd rslib/ftl && scripts/fetch-latest-translations)
(cd qt/ftl && scripts/fetch-latest-translations)
(cd qt/i18n && ./pull-git)
.PHONY: push-i18n
push-i18n: pull-i18n
(cd rslib/ftl && scripts/upload-latest-templates)
(cd qt/ftl && scripts/upload-latest-templates)
(cd qt/i18n && ./sync-po-git)

View file

@ -104,7 +104,7 @@ You can do this automatically by adding the following into
.git/hooks/pre-commit or .git/hooks/pre-push and making it executable. .git/hooks/pre-commit or .git/hooks/pre-push and making it executable.
#!/bin/bash #!/bin/bash
set -e set -eo pipefail
make check make check
You may need to adjust the PATH variable so that things like a local install You may need to adjust the PATH variable so that things like a local install

View file

@ -516,7 +516,7 @@ def test_nextIvl():
assert ni(c, 3) == 21600000 assert ni(c, 3) == 21600000
# (* 100 2.5 1.3 86400)28080000.0 # (* 100 2.5 1.3 86400)28080000.0
assert ni(c, 4) == 28080000 assert ni(c, 4) == 28080000
assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.83mo" assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.8mo"
def test_misc(): def test_misc():

View file

@ -613,7 +613,7 @@ def test_nextIvl():
assert ni(c, 3) == 21600000 assert ni(c, 3) == 21600000
# (* 100 2.5 1.3 86400)28080000.0 # (* 100 2.5 1.3 86400)28080000.0
assert ni(c, 4) == 28080000 assert ni(c, 4) == 28080000
assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.83mo" assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.8mo"
def test_bury(): def test_bury():

View file

@ -6,6 +6,7 @@ from operator import itemgetter
import aqt import aqt
from anki.consts import NEW_CARDS_RANDOM from anki.consts import NEW_CARDS_RANDOM
from anki.lang import _, ngettext from anki.lang import _, ngettext
from aqt import gui_hooks
from aqt.qt import * from aqt.qt import *
from aqt.utils import ( from aqt.utils import (
askUser, askUser,
@ -28,6 +29,7 @@ class DeckConf(QDialog):
self._origNewOrder = None self._origNewOrder = None
self.form = aqt.forms.dconf.Ui_Dialog() self.form = aqt.forms.dconf.Ui_Dialog()
self.form.setupUi(self) self.form.setupUi(self)
gui_hooks.deck_conf_did_setup_ui_form(self)
self.mw.checkpoint(_("Options")) self.mw.checkpoint(_("Options"))
self.setupCombos() self.setupCombos()
self.setupConfs() self.setupConfs()
@ -40,6 +42,7 @@ class DeckConf(QDialog):
self.setWindowTitle(_("Options for %s") % self.deck["name"]) self.setWindowTitle(_("Options for %s") % self.deck["name"])
# qt doesn't size properly with altered fonts otherwise # qt doesn't size properly with altered fonts otherwise
restoreGeom(self, "deckconf", adjustSize=True) restoreGeom(self, "deckconf", adjustSize=True)
gui_hooks.deck_conf_will_show(self)
self.show() self.show()
self.exec_() self.exec_()
saveGeom(self, "deckconf") saveGeom(self, "deckconf")
@ -218,6 +221,7 @@ class DeckConf(QDialog):
f.replayQuestion.setChecked(c.get("replayq", True)) f.replayQuestion.setChecked(c.get("replayq", True))
# description # description
f.desc.setPlainText(self.deck["desc"]) f.desc.setPlainText(self.deck["desc"])
gui_hooks.deck_conf_did_load_config(self, self.deck, self.conf)
def onRestore(self): def onRestore(self):
self.mw.progress.start() self.mw.progress.start()
@ -301,6 +305,7 @@ class DeckConf(QDialog):
c["replayq"] = f.replayQuestion.isChecked() c["replayq"] = f.replayQuestion.isChecked()
# description # description
self.deck["desc"] = f.desc.toPlainText() self.deck["desc"] = f.desc.toPlainText()
gui_hooks.deck_conf_will_save_config(self, self.deck, self.conf)
self.mw.col.decks.save(self.deck) self.mw.col.decks.save(self.deck)
self.mw.col.decks.save(self.conf) self.mw.col.decks.save(self.conf)

View file

@ -518,6 +518,114 @@ class _DeckBrowserWillShowOptionsMenuHook:
deck_browser_will_show_options_menu = _DeckBrowserWillShowOptionsMenuHook() deck_browser_will_show_options_menu = _DeckBrowserWillShowOptionsMenuHook()
class _DeckConfDidLoadConfigHook:
"""Called once widget state has been set from deck config"""
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any], None]] = []
def append(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None:
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any)"""
self._hooks.append(cb)
def remove(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None:
if cb in self._hooks:
self._hooks.remove(cb)
def __call__(
self, deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any
) -> None:
for hook in self._hooks:
try:
hook(deck_conf, deck, config)
except:
# if the hook fails, remove it
self._hooks.remove(hook)
raise
deck_conf_did_load_config = _DeckConfDidLoadConfigHook()
class _DeckConfDidSetupUiFormHook:
"""Allows modifying or adding widgets in the deck options UI form"""
_hooks: List[Callable[["aqt.deckconf.DeckConf"], None]] = []
def append(self, cb: Callable[["aqt.deckconf.DeckConf"], None]) -> None:
"""(deck_conf: aqt.deckconf.DeckConf)"""
self._hooks.append(cb)
def remove(self, cb: Callable[["aqt.deckconf.DeckConf"], None]) -> None:
if cb in self._hooks:
self._hooks.remove(cb)
def __call__(self, deck_conf: aqt.deckconf.DeckConf) -> None:
for hook in self._hooks:
try:
hook(deck_conf)
except:
# if the hook fails, remove it
self._hooks.remove(hook)
raise
deck_conf_did_setup_ui_form = _DeckConfDidSetupUiFormHook()
class _DeckConfWillSaveConfigHook:
"""Called before widget state is saved to config"""
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any], None]] = []
def append(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None:
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any)"""
self._hooks.append(cb)
def remove(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None:
if cb in self._hooks:
self._hooks.remove(cb)
def __call__(
self, deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any
) -> None:
for hook in self._hooks:
try:
hook(deck_conf, deck, config)
except:
# if the hook fails, remove it
self._hooks.remove(hook)
raise
deck_conf_will_save_config = _DeckConfWillSaveConfigHook()
class _DeckConfWillShowHook:
"""Allows modifying the deck options dialog before it is shown"""
_hooks: List[Callable[["aqt.deckconf.DeckConf"], None]] = []
def append(self, cb: Callable[["aqt.deckconf.DeckConf"], None]) -> None:
"""(deck_conf: aqt.deckconf.DeckConf)"""
self._hooks.append(cb)
def remove(self, cb: Callable[["aqt.deckconf.DeckConf"], None]) -> None:
if cb in self._hooks:
self._hooks.remove(cb)
def __call__(self, deck_conf: aqt.deckconf.DeckConf) -> None:
for hook in self._hooks:
try:
hook(deck_conf)
except:
# if the hook fails, remove it
self._hooks.remove(hook)
raise
deck_conf_will_show = _DeckConfWillShowHook()
class _EditorDidFireTypingTimerHook: class _EditorDidFireTypingTimerHook:
_hooks: List[Callable[["anki.notes.Note"], None]] = [] _hooks: List[Callable[["anki.notes.Note"], None]] = []

View file

@ -2,6 +2,7 @@
# #
# build mo files # build mo files
# #
set -eo pipefail
targetDir="../aqt_data/locale/gettext" targetDir="../aqt_data/locale/gettext"
mkdir -p $targetDir mkdir -p $targetDir
@ -9,9 +10,9 @@ mkdir -p $targetDir
echo "Compiling *.po..." echo "Compiling *.po..."
for file in po/desktop/*/anki.po for file in po/desktop/*/anki.po
do do
outdir=$(echo $file | \ outdir=$(echo "$file" | \
perl -pe "s%po/desktop/(.*)/anki.po%$targetDir/\1/LC_MESSAGES%") perl -pe "s%po/desktop/(.*)/anki.po%$targetDir/\1/LC_MESSAGES%")
outfile="$outdir/anki.mo" outfile="$outdir/anki.mo"
mkdir -p $outdir mkdir -p $outdir
msgmerge --for-msgfmt $file po/desktop/anki.pot | msgfmt - --output-file=$outfile msgmerge -q "$file" po/desktop/anki.pot | msgfmt - --output-file="$outfile"
done done

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
set -e set -eo pipefail
out=../aqt_data/locale/qt out=../aqt_data/locale/qt
mkdir -p "$out" mkdir -p "$out"

View file

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -eo pipefail
# pull any pending changes from git repos # pull any pending changes from git repos
./pull-git ./pull-git

View file

@ -2,7 +2,7 @@
# #
# update template .pot file from source code strings # update template .pot file from source code strings
# #
set -eo pipefail
topDir=$(dirname $0)/../.. topDir=$(dirname $0)/../..
cd $topDir cd $topDir

View file

@ -4,7 +4,7 @@
# should be on the path. # should be on the path.
# #
set -e set -eo pipefail
if [ ! -d "designer" ] if [ ! -d "designer" ]
then then

View file

@ -121,6 +121,28 @@ hooks = [
legacy_hook="prepareQA", legacy_hook="prepareQA",
doc="Can modify card text before review/preview.", doc="Can modify card text before review/preview.",
), ),
# Deck options
###################
Hook(
name="deck_conf_did_setup_ui_form",
args=["deck_conf: aqt.deckconf.DeckConf"],
doc="Allows modifying or adding widgets in the deck options UI form",
),
Hook(
name="deck_conf_will_show",
args=["deck_conf: aqt.deckconf.DeckConf"],
doc="Allows modifying the deck options dialog before it is shown",
),
Hook(
name="deck_conf_did_load_config",
args=["deck_conf: aqt.deckconf.DeckConf", "deck: Any", "config: Any"],
doc="Called once widget state has been set from deck config",
),
Hook(
name="deck_conf_will_save_config",
args=["deck_conf: aqt.deckconf.DeckConf", "deck: Any", "config: Any"],
doc="Called before widget state is saved to config",
),
# Browser # Browser
################### ###################
Hook( Hook(

View file

@ -31,8 +31,8 @@ coarsetime = "=0.1.11"
utime = "0.2.1" utime = "0.2.1"
serde-aux = "0.6.1" serde-aux = "0.6.1"
unic-langid = { version = "0.8.0", features = ["macros"] } unic-langid = { version = "0.8.0", features = ["macros"] }
fluent = "0.10.2" fluent = { git = "https://github.com/ankitects/fluent-rs.git", branch="32bit-panic" }
intl-memoizer = "0.3.0" intl-memoizer = { git = "https://github.com/ankitects/fluent-rs.git", branch="32bit-panic" }
num-format = "0.4.0" num-format = "0.4.0"
[target.'cfg(target_vendor="apple")'.dependencies] [target.'cfg(target_vendor="apple")'.dependencies]

View file

@ -15,6 +15,8 @@ fn get_identifiers(ftl_text: &str) -> Vec<String> {
} }
} }
idents.sort();
idents idents
} }

View file

@ -323,7 +323,11 @@ fn set_bundle_formatter_for_langs<T>(bundle: &mut FluentBundle<T>, langs: &[Lang
let num_formatter = NumberFormatter::new(langs); let num_formatter = NumberFormatter::new(langs);
let formatter = move |val: &FluentValue, _intls: &Mutex<IntlLangMemoizer>| -> Option<String> { let formatter = move |val: &FluentValue, _intls: &Mutex<IntlLangMemoizer>| -> Option<String> {
match val { match val {
FluentValue::Number(n) => Some(num_formatter.format(n.value)), FluentValue::Number(n) => {
let mut num = n.clone();
num.options.maximum_fraction_digits = Some(2);
Some(num_formatter.format(num.as_string().to_string()))
}
_ => None, _ => None,
} }
}; };
@ -371,17 +375,12 @@ impl NumberFormatter {
} }
} }
fn format(&self, num: f64) -> String { /// Given a pre-formatted number, change the decimal separator as appropriate.
// is it an integer? fn format(&self, num: String) -> String {
if (num - num.round()).abs() < std::f64::EPSILON { if self.decimal_separator != "." {
num.to_string() num.replace(".", self.decimal_separator)
} else { } else {
// currently we hard-code to 2 decimal places num
let mut formatted = format!("{:.2}", num);
if self.decimal_separator != "." {
formatted = formatted.replace(".", self.decimal_separator)
}
formatted
} }
} }
} }
@ -395,11 +394,8 @@ mod test {
#[test] #[test]
fn numbers() { fn numbers() {
let fmter = NumberFormatter::new(&[]);
assert_eq!(&fmter.format(1.0), "1");
assert_eq!(&fmter.format(1.007), "1.01");
let fmter = NumberFormatter::new(&[langid!("pl-PL")]); let fmter = NumberFormatter::new(&[langid!("pl-PL")]);
assert_eq!(&fmter.format(1.007), "1,01"); assert_eq!(&fmter.format("1.007".to_string()), "1,007");
} }
#[test] #[test]
@ -414,7 +410,7 @@ mod test {
assert_eq!( assert_eq!(
i18n.tr_("two-args-key", Some(tr_args!["one"=>1.1, "two"=>"2"])), i18n.tr_("two-args-key", Some(tr_args!["one"=>1.1, "two"=>"2"])),
"two args: 1.10 and 2" "two args: 1.1 and 2"
); );
assert_eq!( assert_eq!(
@ -423,7 +419,7 @@ mod test {
); );
assert_eq!( assert_eq!(
i18n.tr_("plural", Some(tr_args!["hats"=>1.1])), i18n.tr_("plural", Some(tr_args!["hats"=>1.1])),
"You have 1.10 hats." "You have 1.1 hats."
); );
assert_eq!( assert_eq!(
i18n.tr_("plural", Some(tr_args!["hats"=>3])), i18n.tr_("plural", Some(tr_args!["hats"=>3])),

View file

@ -7,8 +7,9 @@ use crate::i18n::{tr_args, FString, I18n};
pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String { pub fn answer_button_time(seconds: f32, i18n: &I18n) -> String {
let span = Timespan::from_secs(seconds).natural_span(); let span = Timespan::from_secs(seconds).natural_span();
let amount = match span.unit() { let amount = match span.unit() {
TimespanUnit::Months | TimespanUnit::Years => span.as_unit(), // months/years shown with 1 decimal place
// we don't show fractional values except for months/years TimespanUnit::Months | TimespanUnit::Years => (span.as_unit() * 10.0).round() / 10.0,
// other values shown without decimals
_ => span.as_unit().round(), _ => span.as_unit().round(),
}; };
let args = tr_args!["amount" => amount]; let args = tr_args!["amount" => amount];
@ -173,7 +174,7 @@ mod test {
let i18n = I18n::new(&["zz"], ""); let i18n = I18n::new(&["zz"], "");
assert_eq!(answer_button_time(30.0, &i18n), "30s"); assert_eq!(answer_button_time(30.0, &i18n), "30s");
assert_eq!(answer_button_time(70.0, &i18n), "1m"); assert_eq!(answer_button_time(70.0, &i18n), "1m");
assert_eq!(answer_button_time(1.1 * MONTH, &i18n), "1.10mo"); assert_eq!(answer_button_time(1.1 * MONTH, &i18n), "1.1mo");
} }
#[test] #[test]
@ -181,7 +182,7 @@ mod test {
let i18n = I18n::new(&["zz"], ""); let i18n = I18n::new(&["zz"], "");
assert_eq!(time_span(1.0, &i18n), "1 second"); assert_eq!(time_span(1.0, &i18n), "1 second");
assert_eq!(time_span(30.0, &i18n), "30 seconds"); assert_eq!(time_span(30.0, &i18n), "30 seconds");
assert_eq!(time_span(90.0, &i18n), "1.50 minutes"); assert_eq!(time_span(90.0, &i18n), "1.5 minutes");
} }
#[test] #[test]