App-InvestSim

 view release on metacpan or  search on metacpan

lib/App/InvestSim/GUI.pm  view on Meta::CPAN

# reference that gets the beautified content of the field.
sub setup_entry {
  my ($widget, $var, $format, $validate, $textvar) = @_;
  my $right_justified = (Tkx::SplitList($widget->m_configure('-justify')))[-1] eq 'right';
  my $refresh = sub {
    $widget->m_delete(0, 'end');
    $widget->m_insert(0, $format->($$var));
    $$textvar = $format->($$var) if $textvar;
  };
  push @all_refresh_actions, $refresh;
  $widget->g_bind("<FocusIn>", sub { focus_in_field($widget, $var, $right_justified) });
  $widget->g_bind("<FocusOut>", sub { focus_out_field($widget, $var, $right_justified, $refresh) });
  # The validation function will receive the new string and the event 'key' or
  # 'forced' (could be 'focusin' or 'focusout' but we don't validate on these
  # event).
  $widget->m_configure(-validate => 'none', -validatecommand => [ sub { $has_changes = 1; $validate->(@_) }, Tkx::Ev('%P', '%V')]);
}

my $currently_selected;
sub set_core_table_selected_state {
  my ($widget) = @_;
  $currently_selected->m_state('!selected') if $currently_selected;
  $currently_selected = $widget;
  $widget->m_state('selected');
}

# Some global variables for some widgets that are accessed by other methods.
my $modes_combobox;
my @core_display_values;
my $display_table;
my $rent_cap_entry;

# Some variables that are linked to widgets, that are set from elsewhere.
my ($total_rent_text, $rent_cap_text, $pinel_worth_text, $notary_fees_text, $total_invested_text);

# How to format the various modes of the drop-down menu.
my @modes_format;

# Add in the given frame in column 0 and 1 a label and an entry text box in the
# given row that is incremented ($row is a ref to a scalar).
sub add_input_entry {
  my ($frame, $row, $key, $text, $format, $tooltip) = @_;
  my (undef, $validate) = @{$values_config{$key}};
  my $var_ref = \$values{$key};
  $frame->new_ttk__label(-text => "${text} :")
    ->g_grid(-column => 0, -row => $$row, -sticky => "e", -padx => "0 2");
  my $e = $frame->new_ttk__entry(-width => ENTRY_WIDTH);
    $e->g_grid(-column => 1, -row => $$row++, -sticky => "we", -pady => 2);
  setup_entry($e, $var_ref, $format, $validate);
  if ($tooltip) {
    local $Text::Wrap::columns = 50;
    $e->g_tooltip__tooltip(Text::Wrap::fill('', '', $tooltip)); 
  }
}

# Build the main window of the app.
sub build {
  my ($res_dir) = @_;
  Tkx::package_require("tooltip");
  Tkx::option_add("*tearOff", 0); # Disable obsolete tear-off menus
  # For how to find existing background style, see: https://tkdocs.com/tutorial/styles.html#insidestyle
  Tkx::ttk__style('map', 'TEntry', -fieldbackground => ['invalid', '#ff0000']);
  Tkx::ttk__style('map', 'TEntry', -fieldbackground => ['readonly', '#eeeeee']);
  Tkx::ttk__style('configure', 'Invalid.TEntry', -foreground => '#ff0000');
  # This style is used for the entry in the main table, to make it clearer that
  # clicking on them has an effect.
  Tkx::ttk__style('map', 'DataTable.TEntry', -foreground => ['selected', '#0000ff']);
  my $root = Tkx::widget->new(".");
  # We start by hiding the root window, we will show it at the end, when all the
  # UI has been built (to avoid an ugly effect where the user sees each control
  # being added quickly to the UI).
  $root->g_wm_withdraw();
  $root->g_wm_resizable(0, 1);  # Disable resizing of the window x.
  # FIXME: Will have to be adapted to work in the PAR package.
  if (Tkx::expr('$tcl_platform(platform)') eq 'windows') {
    $root->g_wm_iconbitmap(catfile($res_dir, 'icon.ico'));
  } else {
    # On Linux, we can’t read .ico files directly.
    my $icon = Tkx::widget->new(Tkx::image_create_photo());
    $icon->read(catfile($res_dir, 'sources', 'icon_32.png'));
    # We could pass several images of different sizes here.
    $root->g_wm_iconphoto($icon);
  }
  
  # We're copying the font used by the TreeView style and adding an 'bold'
  # option to it, it will be used by the 'total' line.
  my $default_treeview_font = Tkx::ttk__style('lookup', 'TreeView', '-font');
  my $treeview_total_font = Tkx::font('create');
  Tkx::font('configure', $treeview_total_font, Tkx::SplitList(Tkx::font('configure', $default_treeview_font)));
  Tkx::font('configure', $treeview_total_font, -weight => 'bold');
  
  # Build the left bar with various parameters.
  {
    my $frame = $root->new_ttk__frame(-padding => 3);
    $frame->g_grid(-column => 0, -row => 0, -rowspan => 3, -sticky => "we");
    
    my $row = 0;
    for my $c (['invested', "Valeur du bien", \&format_euro, "Prix d'achat du bien, hors frais de notaire."],
               ['tax_rate', "Taux d'imposition marginal", \&format_percent],
               ['base_rent', "Loyer brut initial", \&format_euro],
               ['rent_charges', "Charge et gestion locative", \&format_percent],
               ['rent_increase', "revalorisation loyer", \&format_percent],
               ['duration', "Durée d'investissement", \&format_year],
               ['notary_fees', "Frais de notaire", \&format_percent],
               ['loan_insurance', "Assurance décès du prêt", \&format_percent],
               ['other_rate', "Taux de placement autre", \&format_percent],
               ['surface', "Superficie (pondérée)", \&format_surface, "Surface totale habitable, additionnée, le cas échéant, de la moitié des surfaces annexes dans la limite de 8m² (utilisée seulement pour l'application des plafonds 'Pinel')."]) {
      add_input_entry($frame, \$row, @$c);
    }
  }

  # Build the top bar with the loan duration and rate values.
  my @loan_duration_texts;  # Re-used in the core data table.
  {
    my $frame = $root->new_ttk__frame(-padding => 3);
    $frame->g_grid(-column => 1, -row => 0, -sticky => "nwes");
    $frame->g_grid_columnconfigure(0, -weight => 1); 
      
    $frame->new_ttk__label(-text => "Durée d'emprunt (années)")
      ->g_grid(-column => 0, -row => 0, -sticky => "e");
    $frame->new_ttk__label(-text => "Taux d'emprunt")
      ->g_grid(-column => 0, -row => 1, -sticky => "e");
    my $loan_durations = $values{loan_durations};



( run in 1.663 second using v1.01-cache-2.11-cpan-d8267643d1d )