Adding mode for dyslexia.
This commit is contained in:
parent
da3562de6b
commit
411fddce31
@ -49,9 +49,21 @@ Glib::Object::Introspection->setup(
|
|||||||
has headerbar => ( is => 'rw', );
|
has headerbar => ( is => 'rw', );
|
||||||
has _on_resize_lesson => ( is => 'rw', );
|
has _on_resize_lesson => ( is => 'rw', );
|
||||||
|
|
||||||
has _gresources_path => ( is => 'lazy', );
|
has _gresources_path => ( is => 'lazy', );
|
||||||
has _window => ( is => 'rw' );
|
has _window => ( is => 'rw' );
|
||||||
has _on_resize_triggers => ( is => 'ro', default => sub { {}; } );
|
has _on_resize_triggers => ( is => 'ro', default => sub { {}; } );
|
||||||
|
has accessibility => ( is => 'lazy' );
|
||||||
|
has characters => ( is => 'lazy' );
|
||||||
|
|
||||||
|
sub _build_characters($self) {
|
||||||
|
require JapaChar::Characters;
|
||||||
|
return JapaChar::Characters->new;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _build_accessibility($self) {
|
||||||
|
require JapaChar::Accessibility;
|
||||||
|
return JapaChar::Accessibility->new(app => $self);
|
||||||
|
}
|
||||||
|
|
||||||
sub _build__gresources_path($self) {
|
sub _build__gresources_path($self) {
|
||||||
my $root = path(__FILE__)->parent->parent;
|
my $root = path(__FILE__)->parent->parent;
|
||||||
|
@ -16,6 +16,9 @@ my $option_populated = 'populated_basic_characters';
|
|||||||
require JapaChar::DB;
|
require JapaChar::DB;
|
||||||
require JapaChar::Schema;
|
require JapaChar::Schema;
|
||||||
|
|
||||||
|
has is_repeated => ( is => 'rw', default => sub { 0 } );
|
||||||
|
has _times_repeated => ( is => 'rw', default => sub { 0 });
|
||||||
|
|
||||||
sub populate_basic_characters($self) {
|
sub populate_basic_characters($self) {
|
||||||
my $dbh = JapaChar::DB->connect;
|
my $dbh = JapaChar::DB->connect;
|
||||||
my $result = $dbh->selectrow_hashref(
|
my $result = $dbh->selectrow_hashref(
|
||||||
@ -40,11 +43,10 @@ sub _populate_type( $self, $type ) {
|
|||||||
my $kana = $char->{kana};
|
my $kana = $char->{kana};
|
||||||
my $romanji = $char->{roumaji};
|
my $romanji = $char->{roumaji};
|
||||||
next if $romanji =~ /pause/i;
|
next if $romanji =~ /pause/i;
|
||||||
push @array_for_insertion, { value => $kana, romanji => $romanji, type => $type };
|
push @array_for_insertion,
|
||||||
|
{ value => $kana, romanji => $romanji, type => $type };
|
||||||
}
|
}
|
||||||
$basic_character_resultset->populate([
|
$basic_character_resultset->populate( [@array_for_insertion] );
|
||||||
@array_for_insertion
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _get_characters_of_type( $self, $type ) {
|
sub _get_characters_of_type( $self, $type ) {
|
||||||
@ -60,7 +62,7 @@ sub get_4_incorrect_answers( $self, $char ) {
|
|||||||
JapaChar::Schema->Schema->resultset('BasicCharacter');
|
JapaChar::Schema->Schema->resultset('BasicCharacter');
|
||||||
my @bad_answers = $basic_character_resultset->search(
|
my @bad_answers = $basic_character_resultset->search(
|
||||||
{
|
{
|
||||||
type => $char->type,
|
type => $char->type,
|
||||||
value => { '!=', $char->value },
|
value => { '!=', $char->value },
|
||||||
romanji => { '!=', $char->romanji },
|
romanji => { '!=', $char->romanji },
|
||||||
-bool => 'started',
|
-bool => 'started',
|
||||||
@ -79,9 +81,7 @@ sub _next_review_char( $self, $type = undef ) {
|
|||||||
my @chars = $basic_character_resultset->search(
|
my @chars = $basic_character_resultset->search(
|
||||||
{
|
{
|
||||||
score => { '>=' => 100 },
|
score => { '>=' => 100 },
|
||||||
(
|
( ( defined $type ) ? ( type => $type, ) : () )
|
||||||
( defined $type ) ? ( type => $type, ) : ()
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
order_by => { -asc => \'RANDOM()' },
|
order_by => { -asc => \'RANDOM()' },
|
||||||
@ -94,22 +94,59 @@ sub _next_review_char( $self, $type = undef ) {
|
|||||||
return $chars[0];
|
return $chars[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
sub next_char( $self, $type = undef ) {
|
sub _try_next_char_dyslexia($self, $type = undef) {
|
||||||
|
my $next_repeated_character = $self->_next_repeated_character($type);
|
||||||
|
if ( !defined $next_repeated_character ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$self->is_repeated(1);
|
||||||
|
$self->_times_repeated($self->_times_repeated + 1);
|
||||||
|
if ($self->_times_repeated > 4) {
|
||||||
|
$self->is_repeated(0);
|
||||||
|
$self->_times_repeated(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return $next_repeated_character;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub last_repeated($self) {
|
||||||
|
return $self->_times_repeated >= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub next_char( $self, $accesibility, $type = undef ) {
|
||||||
|
if ( $accesibility->is_dyslexia ) {
|
||||||
|
my $dyslexia_char = $self->_try_next_char_dyslexia;
|
||||||
|
return $dyslexia_char if defined $dyslexia_char;
|
||||||
|
}
|
||||||
|
$self->is_repeated(0);
|
||||||
my $next_review = $self->_next_review_char($type);
|
my $next_review = $self->_next_review_char($type);
|
||||||
my $next_learning = $self->_next_learning_char($type);
|
my $next_learning = $self->_next_learning_char($type);
|
||||||
if ( !defined $next_review ) {
|
if ( !defined $next_review ) {
|
||||||
return $next_learning;
|
return $next_learning;
|
||||||
}
|
}
|
||||||
if ( !defined $next_learning) {
|
if ( !defined $next_learning ) {
|
||||||
return $next_review;
|
return $next_review;
|
||||||
}
|
}
|
||||||
my $rng = JapaChar::Random->new->get(1, 100);
|
my $rng = JapaChar::Random->new->get( 1, 100 );
|
||||||
if ( $rng > 20 ) {
|
if ( $rng > 20 ) {
|
||||||
return $next_learning;
|
return $next_learning;
|
||||||
}
|
}
|
||||||
return $next_review;
|
return $next_review;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub _next_repeated_character( $self, $type = undef ) {
|
||||||
|
my $basic_character_resultset =
|
||||||
|
JapaChar::Schema->Schema->resultset('BasicCharacter');
|
||||||
|
my ($char) = $basic_character_resultset->search(
|
||||||
|
{
|
||||||
|
consecutive_failures => { '>=' => 3 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order_by => { -asc => 'id' },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return $char;
|
||||||
|
}
|
||||||
|
|
||||||
sub _next_learning_char( $self, $type = undef ) {
|
sub _next_learning_char( $self, $type = undef ) {
|
||||||
$self->populate_basic_characters;
|
$self->populate_basic_characters;
|
||||||
@ -120,9 +157,7 @@ sub _next_learning_char( $self, $type = undef ) {
|
|||||||
my @new_chars = $basic_character_resultset->search(
|
my @new_chars = $basic_character_resultset->search(
|
||||||
{
|
{
|
||||||
-not_bool => 'started',
|
-not_bool => 'started',
|
||||||
(
|
( ( defined $type ) ? ( type => $type, ) : () )
|
||||||
( defined $type ) ? ( type => $type, ) : ()
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
order_by => { -asc => 'id' },
|
order_by => { -asc => 'id' },
|
||||||
@ -143,9 +178,7 @@ sub _retrieve_started_chars_not_finished( $self, $type ) {
|
|||||||
JapaChar::Schema->Schema->resultset('BasicCharacter');
|
JapaChar::Schema->Schema->resultset('BasicCharacter');
|
||||||
return $basic_character_resultset->search(
|
return $basic_character_resultset->search(
|
||||||
{
|
{
|
||||||
(
|
( ( defined $type ) ? ( type => $type, ) : () ),
|
||||||
( defined $type ) ? ( type => $type, ) : ()
|
|
||||||
),
|
|
||||||
score => { '<' => 100 },
|
score => { '<' => 100 },
|
||||||
-bool => 'started',
|
-bool => 'started',
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ sub MIGRATIONS {
|
|||||||
consecutive_success INTEGER NOT NULL DEFAULT 0
|
consecutive_success INTEGER NOT NULL DEFAULT 0
|
||||||
);',
|
);',
|
||||||
'INSERT INTO options (name, value) VALUES (\'user_score\', \'0\');',
|
'INSERT INTO options (name, value) VALUES (\'user_score\', \'0\');',
|
||||||
|
'ALTER TABLE basic_characters ADD consecutive_failures INTEGER NOT NULL DEFAULT 0',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
1;
|
1;
|
||||||
|
@ -21,7 +21,7 @@ __PACKAGE__->add_columns(
|
|||||||
value => {
|
value => {
|
||||||
data_type => 'TEXT',
|
data_type => 'TEXT',
|
||||||
is_nullable => 0,
|
is_nullable => 0,
|
||||||
accessor => '_value',
|
accessor => '_value',
|
||||||
},
|
},
|
||||||
romanji => {
|
romanji => {
|
||||||
data_type => 'TEXT',
|
data_type => 'TEXT',
|
||||||
@ -43,10 +43,14 @@ __PACKAGE__->add_columns(
|
|||||||
data_type => 'INTEGER',
|
data_type => 'INTEGER',
|
||||||
is_nullable => 1,
|
is_nullable => 1,
|
||||||
},
|
},
|
||||||
|
consecutive_failures => {
|
||||||
|
data_type => 'INTEGER',
|
||||||
|
is_nullable => 1,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
sub value($self, $value = undef) {
|
sub value( $self, $value = undef ) {
|
||||||
if (defined $value) {
|
if ( defined $value ) {
|
||||||
$self->_value($value);
|
$self->_value($value);
|
||||||
}
|
}
|
||||||
return decode 'utf-8', $self->_value;
|
return decode 'utf-8', $self->_value;
|
||||||
@ -55,41 +59,45 @@ sub value($self, $value = undef) {
|
|||||||
__PACKAGE__->set_primary_key('id');
|
__PACKAGE__->set_primary_key('id');
|
||||||
|
|
||||||
sub fail($self) {
|
sub fail($self) {
|
||||||
my $score = $self->score;
|
my $score = $self->score;
|
||||||
my $consecutive_success = 0;
|
my $consecutive_success = 0;
|
||||||
|
my $consecutive_failures = $self->consecutive_failures + 1;
|
||||||
$score -= 25;
|
$score -= 25;
|
||||||
if ( $score < 0 ) {
|
if ( $score < 0 ) {
|
||||||
$score = 0;
|
$score = 0;
|
||||||
}
|
}
|
||||||
$self->update(
|
$self->update(
|
||||||
{
|
{
|
||||||
score => $score,
|
score => $score,
|
||||||
consecutive_success => 0
|
consecutive_failures => $consecutive_failures,
|
||||||
|
consecutive_success => 0,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get($self, $what) {
|
sub get( $self, $what ) {
|
||||||
if ($what eq 'kana') {
|
if ( $what eq 'kana' ) {
|
||||||
return $self->value;
|
return $self->value;
|
||||||
}
|
}
|
||||||
if ($what eq 'romanji') {
|
if ( $what eq 'romanji' ) {
|
||||||
return $self->romanji;
|
return $self->romanji;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub success($self) {
|
sub success($self) {
|
||||||
my $score = $self->score;
|
my $score = $self->score;
|
||||||
my $consecutive_success = $self->consecutive_success + 1;
|
my $consecutive_success = $self->consecutive_success + 1;
|
||||||
|
my $consecutive_failures = 0;
|
||||||
$score += 5 + 10 * $consecutive_success;
|
$score += 5 + 10 * $consecutive_success;
|
||||||
if ( $score > 130 ) {
|
if ( $score > 130 ) {
|
||||||
$score = 130;
|
$score = 130;
|
||||||
}
|
}
|
||||||
$self->update(
|
$self->update(
|
||||||
{
|
{
|
||||||
score => $score,
|
score => $score,
|
||||||
consecutive_success => $consecutive_success,
|
consecutive_success => $consecutive_success,
|
||||||
|
consecutive_failures => $consecutive_failures,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ use JapaChar::Characters;
|
|||||||
use Pango;
|
use Pango;
|
||||||
use JapaChar::Random;
|
use JapaChar::Random;
|
||||||
use JapaChar::Score;
|
use JapaChar::Score;
|
||||||
|
use Digest::SHA qw(sha1_hex);
|
||||||
|
use Encode qw(encode);
|
||||||
|
|
||||||
use Glib::IO;
|
use Glib::IO;
|
||||||
|
|
||||||
@ -95,9 +97,10 @@ sub _new_challenge_romanji($self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub _new_challenge_generic_code( $self, $show, $guess, $can_be_typed = 0 ) {
|
sub _new_challenge_generic_code( $self, $show, $guess, $can_be_typed = 0 ) {
|
||||||
my $type = $self->_type;
|
my $type = $self->_type;
|
||||||
my $grid = $self->_create_grid_challenge;
|
my $grid = $self->_create_grid_challenge;
|
||||||
my $char = JapaChar::Characters->new->next_char($type);
|
my $char =
|
||||||
|
$self->_app->characters->next_char( $self->_app->accessibility, $type );
|
||||||
my $kana_label = $self->_get_label_featured_character( $char->get($show) );
|
my $kana_label = $self->_get_label_featured_character( $char->get($show) );
|
||||||
my $rng = JapaChar::Random->new->get( 1, 100 );
|
my $rng = JapaChar::Random->new->get( 1, 100 );
|
||||||
|
|
||||||
@ -205,7 +208,13 @@ sub _get_label_featured_character( $self, $text ) {
|
|||||||
my $label = Gtk::Label->new($text);
|
my $label = Gtk::Label->new($text);
|
||||||
my $attr_list = Pango::AttrList->new;
|
my $attr_list = Pango::AttrList->new;
|
||||||
my $size = Pango::AttrSize->new( 72 * PANGO_SCALE );
|
my $size = Pango::AttrSize->new( 72 * PANGO_SCALE );
|
||||||
|
my $color = Pango::Color->new;
|
||||||
|
|
||||||
|
my $hex_color = sha1_hex(encode 'utf-8', $text);
|
||||||
|
my ($r, $g, $b) = map { $_ = hex $_; ($_ << 8) | $_ } $hex_color =~ /(..)(..)(..)/;
|
||||||
|
my $color_attr = Pango::AttrForeground->new($r, $g, $b);
|
||||||
$attr_list->insert($size);
|
$attr_list->insert($size);
|
||||||
|
$attr_list->insert($color_attr);
|
||||||
$label->set_attributes($attr_list);
|
$label->set_attributes($attr_list);
|
||||||
$label->set_halign('center');
|
$label->set_halign('center');
|
||||||
return $label;
|
return $label;
|
||||||
@ -314,18 +323,24 @@ sub _on_click_continue_button( $self, $grid, $char, $guess ) {
|
|||||||
}
|
}
|
||||||
$self->_first_press_continue(0);
|
$self->_first_press_continue(0);
|
||||||
my $label_feedback;
|
my $label_feedback;
|
||||||
|
my $is_repeating = $self->_app->characters->is_repeated;
|
||||||
{
|
{
|
||||||
if ( $self->_final_answer eq $char->get($guess) ) {
|
if ( $self->_final_answer eq $char->get($guess) ) {
|
||||||
$label_feedback = Gtk::Label->new('You are doing it great.');
|
$label_feedback = Gtk::Label->new('You are doing it great.');
|
||||||
$label_feedback->add_css_class('success');
|
$label_feedback->add_css_class('success');
|
||||||
$self->lesson->add_one_success;
|
$self->lesson->add_one_success;
|
||||||
$char->success;
|
$char->success if !$is_repeating;
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
$label_feedback = Gtk::Label->new(
|
$label_feedback = Gtk::Label->new(
|
||||||
'Meck!! The correct answer is ' . $char->get($guess) );
|
'Meck!! The correct answer is ' . $char->get($guess) );
|
||||||
$label_feedback->add_css_class('error');
|
$label_feedback->add_css_class('error');
|
||||||
$char->fail;
|
$char->fail if !$is_repeating;
|
||||||
|
}
|
||||||
|
if ( $is_repeating && $self->_app->characters->last_repeated ) {
|
||||||
|
|
||||||
|
# TODO This is not ideal.
|
||||||
|
$self->success;
|
||||||
}
|
}
|
||||||
my $attr_list = Pango::AttrList->new;
|
my $attr_list = Pango::AttrList->new;
|
||||||
my $size = Pango::AttrSize->new( 23 * $self->_app->get_width );
|
my $size = Pango::AttrSize->new( 23 * $self->_app->get_width );
|
||||||
|
@ -106,12 +106,21 @@ sub run($self) {
|
|||||||
$grid->attach( $box_score_basic_lesson, 0, 0, 5, 1 );
|
$grid->attach( $box_score_basic_lesson, 0, 0, 5, 1 );
|
||||||
$button_start_basic_lesson->set_valign('end');
|
$button_start_basic_lesson->set_valign('end');
|
||||||
$button_start_basic_lesson->set_halign('center');
|
$button_start_basic_lesson->set_halign('center');
|
||||||
|
my $button_assisted_mode = Gtk::Button->new_with_label('Assisted Mode');
|
||||||
$box->set_margin_top(40);
|
$box->set_margin_top(40);
|
||||||
$box->append($button_start_hiragana_lesson);
|
$box->append($button_start_hiragana_lesson);
|
||||||
$box->append($button_start_katakana_lesson);
|
$box->append($button_start_katakana_lesson);
|
||||||
$box->set_valign('start');
|
$box->set_valign('start');
|
||||||
$box->set_halign('center');
|
$box->set_halign('center');
|
||||||
$grid->attach( $box, 0, 1, 5, 1 );
|
$grid->attach( $box, 0, 1, 5, 1 );
|
||||||
|
$grid->attach( $button_assisted_mode, 0, 2, 5, 1 );
|
||||||
|
$button_assisted_mode->signal_connect('clicked', sub {
|
||||||
|
$self->app->accessibility->show_assisted_mode_selection;
|
||||||
|
});
|
||||||
|
$button_assisted_mode->set_vexpand(1);
|
||||||
|
$button_assisted_mode->set_hexpand(1);
|
||||||
|
$button_assisted_mode->set_valign('center');
|
||||||
|
$button_assisted_mode->set_halign('center');
|
||||||
$self->app->window_set_child($grid);
|
$self->app->window_set_child($grid);
|
||||||
}
|
}
|
||||||
1;
|
1;
|
||||||
|
Loading…
Reference in New Issue
Block a user