~zanneth/Octahedron

96168a364e4abb3f17b4b71af57690e62acd2afc — James Magahern 1 year, 3 months ago edd0011
Adds a divide by two button to CreateDebtRequestDialog

Adds a divide by two button next to the "amount" field, and implements
some special behavior for when the button resets or when the field changes.
2 files changed, 143 insertions(+), 51 deletions(-)

M gtk/res/create-debt-request.ui
M gtk/src/create_debt_request_dialog.rs
M gtk/res/create-debt-request.ui => gtk/res/create-debt-request.ui +76 -44
@@ 1,32 1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkDialog" id="create-debt-request-dialog">
    <property name="can_focus">False</property>
    <property name="border_width">5</property>
    <property name="can-focus">False</property>
    <property name="border-width">5</property>
    <property name="resizable">False</property>
    <property name="icon_name">com.zanneth.octahedron</property>
    <property name="type_hint">dialog</property>
    <child type="titlebar">
      <placeholder/>
    </child>
    <property name="icon-name">com.zanneth.octahedron</property>
    <property name="type-hint">dialog</property>
    <child internal-child="vbox">
      <object class="GtkBox">
        <property name="can_focus">False</property>
        <property name="can-focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
            <property name="can-focus">False</property>
            <property name="layout-style">end</property>
            <child>
              <object class="GtkButton" id="cancel-button">
                <property name="label">gtk-cancel</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
                <property name="can-focus">True</property>
                <property name="receives-default">True</property>
                <property name="use-stock">True</property>
              </object>
              <packing>
                <property name="expand">True</property>


@@ 38,9 35,9 @@
              <object class="GtkButton" id="ok-button">
                <property name="label">gtk-ok</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
                <property name="can-focus">True</property>
                <property name="receives-default">True</property>
                <property name="use-stock">True</property>
                <accelerator key="Return" signal="activate"/>
              </object>
              <packing>


@@ 57,16 54,17 @@
          </packing>
        </child>
        <child>
          <!-- n-columns=3 n-rows=3 -->
          <object class="GtkGrid">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="margin_bottom">10</property>
            <property name="row_spacing">6</property>
            <property name="column_spacing">6</property>
            <property name="can-focus">False</property>
            <property name="margin-bottom">10</property>
            <property name="row-spacing">6</property>
            <property name="column-spacing">6</property>
            <child>
              <object class="GtkLabel" id="header-label">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="can-focus">False</property>
                <property name="label" translatable="yes">Create Debt</property>
                <property name="xalign">0</property>
                <attributes>


@@ 74,60 72,94 @@
                </attributes>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">0</property>
                <property name="left-attach">0</property>
                <property name="top-attach">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel" id="amount-label">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="can-focus">False</property>
                <property name="label" translatable="yes">Amount:</property>
                <property name="xalign">1</property>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">1</property>
                <property name="left-attach">0</property>
                <property name="top-attach">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkEntry" id="amount-entry">
              <object class="GtkLabel" id="description-label">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hexpand">True</property>
                <property name="input_purpose">number</property>
                <property name="can-focus">False</property>
                <property name="label" translatable="yes">Description:</property>
                <property name="xalign">1</property>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">1</property>
                <property name="left-attach">0</property>
                <property name="top-attach">2</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel" id="description-label">
              <object class="GtkEntry" id="description-entry">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">Description:</property>
                <property name="xalign">1</property>
                <property name="can-focus">True</property>
                <property name="hexpand">True</property>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">2</property>
                <property name="left-attach">1</property>
                <property name="top-attach">2</property>
              </packing>
            </child>
            <child>
              <object class="GtkEntry" id="description-entry">
              <object class="GtkBox">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hexpand">True</property>
                <property name="can-focus">False</property>
                <property name="spacing">6</property>
                <child>
                  <object class="GtkEntry" id="amount-entry">
                    <property name="visible">True</property>
                    <property name="can-focus">True</property>
                    <property name="hexpand">True</property>
                    <property name="input-purpose">number</property>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkToggleButton" id="divide-button">
                    <property name="label" translatable="yes">รท2</property>
                    <property name="visible">True</property>
                    <property name="can-focus">True</property>
                    <property name="receives-default">True</property>
                  </object>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">True</property>
                    <property name="position">1</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="left_attach">1</property>
                <property name="top_attach">2</property>
                <property name="left-attach">1</property>
                <property name="top-attach">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <placeholder/>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>

M gtk/src/create_debt_request_dialog.rs => gtk/src/create_debt_request_dialog.rs +67 -7
@@ 2,6 2,7 @@ use async_channel::Sender;
use gtk::prelude::*;
use gtk::glib;
use glib::clone;
use std::println;
use std::str::FromStr;
use web3::types::U256;



@@ 15,6 16,8 @@ pub enum Message {
enum InternalMessage {
    DialogResponse(gtk::ResponseType),
    EntryChanged,
    AmountModified,
    DividedAmount,
}

pub struct CreateDebtRequestDialog {


@@ 23,6 26,8 @@ pub struct CreateDebtRequestDialog {
    amount_entry:       gtk::Entry,
    description_entry:  gtk::Entry,
    ok_button:          gtk::Button,
    divide_button:      gtk::ToggleButton,
    editing_locked:     bool,
}

impl CreateDebtRequestDialog {


@@ 35,6 40,7 @@ impl CreateDebtRequestDialog {
        let amount_entry: gtk::Entry = builder.object("amount-entry").unwrap();
        let description_entry: gtk::Entry = builder.object("description-entry").unwrap();
        let ok_button: gtk::Button = builder.object("ok-button").unwrap();
        let divide_button: gtk::ToggleButton = builder.object("divide-button").unwrap();
        
        Self {
            tx: tx.clone(),


@@ 42,10 48,12 @@ impl CreateDebtRequestDialog {
            amount_entry,
            description_entry,
            ok_button,
            divide_button,
            editing_locked: false,
        }
    }

    pub fn show(self) {
    pub fn show(mut self) {
        let (tx, rx) = async_channel::unbounded();

        let dialog = self.dialog.clone();


@@ 54,13 62,17 @@ impl CreateDebtRequestDialog {
        }));

        self.amount_entry.connect_changed(clone!(@strong tx => move |_| {
            dispatch_msg!(tx, InternalMessage::EntryChanged);
            dispatch_msg!(tx, InternalMessage::AmountModified);
        }));
        

        self.description_entry.connect_changed(clone!(@strong tx => move |_| {
            dispatch_msg!(tx, InternalMessage::EntryChanged);
        }));

        self.divide_button.connect_clicked(clone!(@strong tx => move |_| {
            dispatch_msg!(tx, InternalMessage::DividedAmount);
        }));

        self.reload_button_states();

        spawn_local(async move {


@@ 74,14 86,26 @@ impl CreateDebtRequestDialog {
        dialog.show_all();
    }

    fn handle_message(&self, msg: &InternalMessage) -> bool {
    fn get_amount(&self) -> U256 {
        let famount = f64::from_str(self.amount_entry.text().as_str()).unwrap_or(0.0);
        return U256::from((famount * 100.0) as u64);
    }

    fn handle_message(&mut self, msg: &InternalMessage) -> bool {
        match msg {
            InternalMessage::DialogResponse(response) => {
                self.handle_dialog_response(response);
                return false
            },

            InternalMessage::EntryChanged => self.reload_button_states()
            InternalMessage::EntryChanged => self.reload_button_states(),

            InternalMessage::AmountModified => {
                self.reload_button_states();
                self.reset_divide_button();
            }

            InternalMessage::DividedAmount => self.apply_divided_amount(),
        }
        
        true


@@ 91,8 115,7 @@ impl CreateDebtRequestDialog {
        match response {
            gtk::ResponseType::Accept => {
                if self.input_state_valid() {
                    let famount = f64::from_str(self.amount_entry.text().as_str()).unwrap_or(0.0);
                    let amount = U256::from((famount * 100.0) as u64);
                    let amount = self.get_amount();
                    let description = self.description_entry.text().to_string();
                    dispatch_msg!(self.tx, Message::CreateDebtRequest { amount, description });
                }


@@ 107,6 130,43 @@ impl CreateDebtRequestDialog {
    fn reload_button_states(&self) {
        self.ok_button.set_sensitive(self.input_state_valid());
    }

    fn apply_divided_amount(&mut self) {
        if self.editing_locked {
            self.editing_locked = false;
            return
        }

        let mut amount = self.get_amount();
        if !self.divide_button.is_active() {
            // If divide button is pressed already, undo division. 
            amount = amount * 2;
        } else {
            // Otherwise, divide by 2. 
            amount = amount / 2;
        }

        // There's no way to tell the difference between user input and programmatic input, so we need 
        // to use editing_locked here to be able to tell the difference. 
        self.editing_locked = true;

        let divided_amount_str = format!("{:.2}", amount.low_u64() as f64 / 100.0);
        self.amount_entry.set_text(divided_amount_str.as_str());
    }

    fn reset_divide_button(&mut self) {
        if !self.divide_button.is_active() {
            return
        }

        if self.editing_locked {
            self.editing_locked = false;
            return
        }

        self.editing_locked = true;
        self.divide_button.set_active(false);
    }
    
    fn input_state_valid(&self) -> bool {
        !self.description_entry.text().is_empty() &&