174 Commits

Author SHA1 Message Date
520b959302 structure update
* rename PolicyCheckView to PolicycheckView
* rename PolicyCheckState to PolicycheckState
* rename menu to popup_menu

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-11-01 20:45:27 +01:00
7d459516d9 module structure update 2
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-11-01 20:19:55 +01:00
c511bdd62f main: reflect new module structure
* include from assets
* get widgets into place
* introduce style extensions (theme_fluent)

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-11-01 20:19:55 +01:00
40f715a79a update project structure
* assets: all resources are bundled in this subdirectory
* widgets: use subdirs to group them by function
  the subdir hold their callbacks (state handling) and views
* theming: update to new theming syntax
* fonts: remove fonts that are provovided by default in OrbTK

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-11-01 20:19:55 +01:00
6ea1f95ef0 main_view: introduce configuration view
* allow to load and save config parameters to ron file

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-11-01 20:19:54 +01:00
8986812038 policycheck_view: update to 0.3.1-alpha4 changes
* lose_focus_on_activate()

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-11-01 20:19:54 +01:00
e0eb0c5c90 policycheck_state: update localization of menu entries
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-11-01 20:19:54 +01:00
0ba99b3fb2 Merge branch 'orbtk-localization' into master
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-10-02 12:09:56 +02:00
f0d5f82549 advotracker: bump release to 0.1.4
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-10-02 11:57:52 +02:00
d2a188eee6 policycheck: update styling, constants and localization
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-28 13:06:50 +02:00
e297c82b85 resources: references used at runtime
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-27 10:44:03 +02:00
f87ab8ce08 advotracker: implement get_lang
* readin language environment and preset the strings

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 21:12:54 +02:00
0c16237c6c callbacks: remove unused global functions
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 19:29:34 +02:00
777fff3776 locales: remove redundant locales in json file
* locales in policy_check.json are included in advotracker.json

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 19:27:30 +02:00
c949b5fe32 Cargo.toml: update package dependencies
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 17:53:19 +02:00
0afe1f3bf8 Nodes.toml: application settings for nodes module
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 17:53:19 +02:00
bf54cb2242 .env: adapt test defaults for environment variables
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 17:53:19 +02:00
5b79229c00 advotracker: change localization handling
* main: adapt resource path for localization information
* data/constants: Stings used in policycheck state/view
* widgets/policycheck_view: change the used localization strings
* callbacks/policycheck_state: change child ID'S to use constants and localization strings

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 17:53:19 +02:00
16a538a805 resources: cleanup unneeded files since
* now using ron based files for localization
* resources are referencing application path names

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 17:53:19 +02:00
a887ffef1f services: adapte/update used strings
* for importp module
* for export module

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 17:52:15 +02:00
65c2da7706 advotracker-db: update to new head
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 17:52:15 +02:00
0467f601f0 locales: adapt language identifiers
* use lang in logging/tracing
* typo cleanup

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-26 17:51:55 +02:00
8a50e1ea78 widget: main_view: add obtk ron based localization support
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-24 10:18:14 +02:00
c54cca5f22 main: include orbtk ron based localization
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-24 10:18:14 +02:00
bf638ac196 callbacks/policycheck_state: convert to new API (orbtk v0.3.1-alpha4)
Before
* let text = *button(&mut ctx.widget()).text();
* let text = Button::get(&mut ctx.widget()).text();
* let text = *button(&mut ctx.widget()).clone_text();
* let text = Button::get(&mut ctx.widget()).clone_text();
* button(&mut ctx.widget()).text_mut().push_str("test");
* Button::get(&mut ctx.widget()).text_mut().push_str("test");
* button(&mut ctx.widget()).set_text(String16::from("test"));
* Button::get(&mut ctx.widget()).set_text(String16::from("test"));

Now
* let text = Button::text_clone(&ctx.widget());
* Button::text_mut(&mut ctx.widget()).push_str("test");
* Button::text_set(&mut ctx.widget(), String16::from("test"));
* ProgressBar::val_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR), new_width);

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-17 16:49:49 +02:00
06e68adce6 callbacks/policycheck_state: convert to new API (orbtk-alpha4)
Before
* let text = *button(&mut ctx.widget()).text();
* let text = Button::get(&mut ctx.widget()).text();
* let text = *button(&mut ctx.widget()).clone_text();
* let text = Button::get(&mut ctx.widget()).clone_text();
* button(&mut ctx.widget()).text_mut().push_str("test");
* Button::get(&mut ctx.widget()).text_mut().push_str("test");
* button(&mut ctx.widget()).set_text(String16::from("test"));
* Button::get(&mut ctx.widget()).set_text(String16::from("test"));

Now
* let text = Button::text_clone(&ctx.widget());
* Button::text_mut(&mut ctx.widget()).push_str("test");
* Button::text_set(&mut ctx.widget(), String16::from("test"));
* ProgressBar::val_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR), new_width);

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-09-17 16:24:02 +02:00
645ea50350 callbacks: update commenting code in policycheck_state
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-28 11:56:22 +02:00
13d56e2e05 examples: new increment_progress_bar source
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-28 11:55:42 +02:00
ef63f62734 Main: update position
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-22 19:08:51 +02:00
5aff69c526 widgets/policycheck_view: template id's
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-22 19:03:52 +02:00
70e2e7f1e5 callbacks/policycheck_state: update theme, popup
* popup handling update
* handling of themes

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-22 19:02:18 +02:00
3697bc567a widgets/main_view: update use statement for our crate
* just a style update

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-15 19:34:37 +02:00
ece95344bf Grid .add() to .push()
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-15 19:34:13 +02:00
69405aaa76 main: initialize orbtk with initialize() functions
* needed at least for wayland client

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-15 19:33:24 +02:00
cd82f62a82 widgets/policycheck: update new syntax for Grid
* Columns,Row use create() functions
* update from .add() to new .push() method

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-15 19:31:51 +02:00
eb7a3608d7 data/structures: derive PartialEq macro
* enable PartialEq needed from new wayland client backend

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-08-15 19:30:32 +02:00
b6b12c45bb u64 update
* policynumber has 10 unsigned integers. usize is 32bit on
  on 32 machines. So we need to use u64 to assign the needed ram
  space to fit numbers like '9999999999'

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 20:55:53 +02:00
9b9afc2b5a examples: advowidgets: update to upstream orbtk example
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 20:55:11 +02:00
094fb6d0ea Node.toml: adapt fonts and app options
* remove unused fonts
* correct typo in app name
* correct active font name

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 20:54:25 +02:00
f983eca9aa widgets: policycheck_view: hinting NumericBox, typo correction
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 19:57:06 +02:00
bceec88741 Cargo.toml: reference to upstream orbtk (branch develop)
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 19:57:06 +02:00
daaa544e93 main: remove unused comments
* implement lazy pointer to struct if orbtk can handle it

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 19:57:06 +02:00
33da48b1b1 stylesheets: use explicit theme name
* Cargo.toml: add feature
* policy_state.rs: add handling of feature for choosen theme
* advotracker_dark.ron: advotrackers dark theme
* advotracker_light.ron: advotrackers light theme

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 19:57:06 +02:00
046812e04f README.md: typo correction
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 19:57:00 +02:00
bd70ad653b main: move theming support to callback code, cleanup
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 19:33:22 +02:00
ac855d0a54 widgets: policycheck_view: global "lang" and styling
* use ron styling
* language variable in struct

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 19:33:22 +02:00
b099064776 services: imports: refine tests in allianzdirectcall.rs
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-24 19:33:14 +02:00
65bec5a4a1 callbacks: introduce theming, i18n truning, introduce menu
* global_state: use global "lang", support env
* policycheck_state: use global "lang", new menu function, ron theming

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-22 18:11:39 +02:00
276c0fb5f0 bin: policycheck: simplified core to check policy number
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-22 18:10:51 +02:00
0f97202fbb widgets: policycheck_menu: update styling
* not used, since menu is dynamically created via function

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-22 18:08:16 +02:00
e91b91a2db i18n: update constants and translation messages
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-22 18:07:43 +02:00
25de70e75c examples: remove unused code
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-22 18:07:11 +02:00
9fffb093c3 resources: stylesheets: switching to ron bases CSS
* remove unused old theme.css files
* advotracker.ron: new ron based theming style
* main.rs: extend the dark theme with our stylesheet

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-20 16:43:51 +02:00
11d12718e8 README.md: update to version 1.0.3
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:33:37 +02:00
6e4ba029b7 Cargo.toml: bump version to 0.1.3
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:21:20 +02:00
fd61f90277 callbacks: policycheck_state refine popup widget handling
* on first access, show a processing popup
* calculate the processing time
* update process status
* show processing time in popup

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:18:27 +02:00
ffa8c18efb services: imports: refine to use SystemTime
* Use standard library to calculate SystemTime and Duration
* exporse results to be accessible in orbtk views

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:18:27 +02:00
3d913e7127 widgets: policycheck_view - use constants
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:17:16 +02:00
8ca257bf5c advotracker: rename 'keys' to 'constants'
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:17:16 +02:00
6907c0c393 callbacks: progressing I18n handling
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:17:16 +02:00
9e1a1e7359 widgets: policycheck_view typo cleanup
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:17:07 +02:00
16f174d53a I18N: update translation constants
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-15 00:16:46 +02:00
4406efc955 data: define constanst to be static strings
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-14 11:11:19 +02:00
20ddd78a8d Cargo.toml: enable cross-compile to target Windows x86_64
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-14 11:09:59 +02:00
863826a294 Cargo.toml: bump version to 0.1.2
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-13 03:43:47 +02:00
53d8f80b75 services: import allianzdirectcall update
* use usize for counter

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-13 03:43:47 +02:00
555a02fcc3 structures: CsvExportRecord type update for policy_number
* policy_number: type reset to usize

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-13 03:43:47 +02:00
4d2427a058 callbacks: policycheck_state update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-13 03:43:38 +02:00
5eeb80cdd4 structure: cosmetic update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-13 03:43:38 +02:00
aa26e6634d widgets: policycheck_view update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-13 03:43:24 +02:00
19ad9198d0 data: keys: update ID's
* ID_POLICY_CHECK_DATA_COUNT_BLOCK
* ID_POLICY_CHECK_PROGRESS_BOX

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>

keys: update

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-13 03:43:24 +02:00
e62fb8e990 I18N: translation update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-13 03:43:12 +02:00
d3d3be1442 I18N: README.md language variants (v0.1.2)
* update contents inside README.md
* provide language variant (english, german)
* document translation handling
* installation: download via git

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:27:26 +02:00
b579d039f1 advotracker: update copyright header
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:27:26 +02:00
c5334ac898 .env: test environment file
* use an .env with testing parameters

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:27:26 +02:00
224606d72e resources: new log and font to create the logo
* svg: scalable logo
* CynthoPro-Regular: font used to create text typo

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:27:26 +02:00
4e0b8166f2 callbacks: policycheck_state update
* experimental handling of a progress bar

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:27:26 +02:00
193f2de7ea widgets: update policycheck_view
* update margins
* add widget IDs
* experimental inclusion of a progress bar

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:27:21 +02:00
e2ced73db9 imports: policy_data_count as required variable
* function call will gain access

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:26:10 +02:00
09ec027e79 data: update widget static for used widget IDs
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:26:08 +02:00
656ffae9bf resources: update customer logo
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:25:49 +02:00
9753c37477 policycheck_state: import data to hashmap
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:25:49 +02:00
f503fc9713 parse_args: remove duplicate test of dotenv variables
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:25:49 +02:00
8564aaaf8d policycheck: i18n, tracing updates
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:25:46 +02:00
947e79eea4 policy_check: remove println macro calls
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:47 +02:00
a9f7c62e3d main: lazy_static prototyping, env handling
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:47 +02:00
c0d8e7a898 main: introduce machine specific handling
* machine_kind: differenciate depending on detected machine OS

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:47 +02:00
7a82640fd6 main: make log level dynamic
* set the log level via environment variable

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:47 +02:00
f9453ee18c main: suppress creation of consol window on MS Windows
* Rust applications on Windows always open a new console window
  if started by double-clicking on the executable.
* since rust 1.18 the the macro suppresses this behaviour

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:47 +02:00
f9dd2e3797 README: update project documentation
* i18n: howto extend translations
* installation: download via git

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:47 +02:00
ea57346119 examples: introduce advowidgets
* merge orbtk example widgets
* rename to advowidgets
* incorporate functionality of wip-orbtk branch
  - using latest material-icons-font code
  - merge bugfix to add ComboBox items
  - merge bugfix to handle ComboList

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:47 +02:00
e7ef33ee40 examples: advowidgets: update to new api syntax
* when addressing widgets properties the new api syntax
  seems more natural. It accesses the properties via a function
  oriented notation:

  - view_name(<ctx.widget()).<entity>_mut()

  - ctx.child("<entity>").set::<type>("<field>", <new_property>)

  - <widget_class>(ctx.child(<entity)).set_<field>(<new_property>)

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:47 +02:00
5a5bbb5d5e stylesheet: add advotracker.css
* redefine resources in a new application stylesheet

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:45 +02:00
e8c7d78de3 callbacks: global_state: save data vector to ron file on disk
* new functionality
* save imported / edited data-structures persitantliy
  on disk to file in 'ron' format

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:21 +02:00
bf0ee18f55 callbacks: global_state: compiler sanitization
* use unspecific lifetimes

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:21 +02:00
b709c90682 callbacks: policy_check: update identifier for tracing
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:21 +02:00
32405504a7 widgets: main_view: activate structure init via default()
* possible, since structure provides access via orbtk

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:17 +02:00
891919b64d imports: allianzdirectcall: use array to verify on startup
* using test dataset

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:02 +02:00
40896c6b8e structures: include reference to orbtk
* make structures accessible for orbtk
* documentation update

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:23:02 +02:00
9beeaa7190 data: keys: cleanup DCES id's and properties
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:22:59 +02:00
7e604f050a locales: remove redundant allianzdirectcal
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:22:40 +02:00
ebaf05132f policycheck_state: set_visibility: make param entity non-required
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:22:36 +02:00
ce7f802d3a policycheck_view: adapt width of policy number text_box
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:22:16 +02:00
d7da1335b1 policycheck_view: brake up bigger widget definitions into sub-groups
* improve maintainability
  - make sub-entities grouped into sub-widgets
  - reference this sub-widgets from inside their parents

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:22:12 +02:00
d6703e8df6 policycheck: introduce policy_check_button_result
* after validation, show suitables glyph and corresponding text
* improve user feedback with corresponding color

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:20:52 +02:00
24b9b08566 policycheck: use material_icons_font
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:14:21 +02:00
274a96b908 policycheck: functionality update
* show count of active hashmap members in GUI
* move import call to init function
* show results of policy_number validation in GUI
* improve tracing information
* disable stdout messages via println!()

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:13:49 +02:00
65e0ce3319 policycheck: initial standalone frontend
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:13:06 +02:00
77d41f733e csv-test: bugfix missing translation string
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:13:06 +02:00
18588ce75f csv-test: remove unneeded references
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:13:06 +02:00
d5788b9848 csv-test: structure path update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:13:06 +02:00
e853975c86 services: update im/exporter and remove unused source
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:13:06 +02:00
bc161878b3 parse_args: make vektor 'viperus' explicit
* when using as a globel, this is more easy to recognize

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:13:06 +02:00
d0c7a16ac7 main_view: set id to be non-required
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:12:55 +02:00
5745a3bd6c advotracker: policy_state update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:12:28 +02:00
7196013bb2 advotracker: get correct crates and modules into scope
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:12:28 +02:00
1aebd4338f advotracker: link to resources
* call from binary-directory will search resource files
  in subdirecotries beneath this directory

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:12:28 +02:00
6f668bf4c3 advotracker: widget update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:12:27 +02:00
97622ba5f5 advotracker: callback: update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:12:27 +02:00
533ae6f8d2 .gitignore: typo correction and commenting user config settings
* we don't save config.toml in upstream

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:12:27 +02:00
23c1875272 database: remove stale submodule
* substitued with 'advetrackerd'

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:12:27 +02:00
02d388c14f advotracker-db: update commit refering to new head
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:11:27 +02:00
016595d702 advotrackerd: introduce new submodule
* orign: https://gitea.networkx.de:50443/rzerres/advotrackerd
* for now, we do have a Microsoft CA which isn't handled as an
  official CA.
  You need to exclude SSL verification to push/pull/clone like this:

* # git config  --file .git/modules/advotrackerd/config --list
* # git config --file .git/modules/advotrackerd/config  http.https://gitea.networkx.de:50443.sslverify false

* # git -c http.https://gitea.networkx.de:50443.sslverify=false submodule add  https://gitea.networkx.de:50443/rzerres/advotrackerd

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:10:49 +02:00
8506b0ce90 advotracker-db: introduce new submodule
* orign: https://gitea.networkx.de:50443/rzerres/advotracker.db
* for now, we do have a Microsoft CA which isn't handled as an
  official CA.
  You need to exclude SSL verification to push/pull/clone like this:

* # git config  --file .git/modules/advotracker-db/config --list
* # git config --file .git/modules/advotracker-db/config  http.https://gitea.networkx.de:50443.sslverify false

* # git -c http.https://gitea.networkx.de:50443.sslverify=false submodule add  https://gitea.networkx.de:50443/rzerres/advotracker-db

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:10:49 +02:00
74fc662a67 Cargo.toml: rebase root of local orbtk sources
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:10:49 +02:00
4f3a63425a Cargo.toml: update repository location
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:10:49 +02:00
42dcd82df2 Cargo.toml: identifier change
* use 'nwx' as company prefix

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:10:49 +02:00
82c1aab492 Cargo.toml: preset the default binary
* use the GUI variant 'advotracker' as the default binary

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:10:48 +02:00
cf375770c1 Corgo.toml: change syntax to reference dependencies
* use explicit style with braces

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:10:48 +02:00
da24fea764 Cargo.toml: add workspace advotracker-db
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 19:10:48 +02:00
321b868bb8 Cargo.toml: comment out orbtk/debug
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:33 +02:00
5f4d01d919 advotracker-framework: restructure source tree
* remove subdirectory backend
  new handling as new submodule advotrackerd
* remove subdirectory frontend
  new hanling as advotracker
* examples: example binaries
* locales: i18n definitions
* src/callbacks: rust state handling for widgets
* src/services: provide service methods (e.g import/export)
* src/widgets: handle frontend definitions
* src/data: provide test-data

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:32 +02:00
8f7be15ef3 services: allianzdirectcall: update process logic
* return value: usize
* import: fill HashMap from source record
* import: fill in vector PolicyData

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:32 +02:00
cddb47b3ad csv-test: update the .env test environment
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:32 +02:00
56aa6ac72c csv-test: include cli flag to provide a test policy number
* TODO: viperus does not offer the input of usize/i64
  therefore we can't use big integer for now

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:32 +02:00
bde94bcce1 csv-test: update test data
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:32 +02:00
8a1f92e44d csv-test: integrate HashMap functionality
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:32 +02:00
267680f547 csv-test: new minimal test datasets
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:32 +02:00
ea4bcaabd6 data.rs: include copy macro for PolicyCode
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:19:32 +02:00
268207318d I18n: update translation patterns
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:18:41 +02:00
5dc3fa1509 Cargo.toml: update umbrella project name, rename submodules
* submodule advotrackerd: provides the database api
* submodule advotracker: the frontend app

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:16:21 +02:00
d57827ca4e examples: csv-test: new testfile that holds enough test records
* no need to parse 40MB
* ~15000 records are more then enough

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:16:16 +02:00
97a5a966d3 advotracker: services substructure
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:48 +02:00
5016cb6b47 advotracker: library definition to use as crate
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:48 +02:00
84e7a86bb3 I18N: update localization file to meet new project name
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:48 +02:00
b7e6d269dc advotracker-framework: Rename 'frontend' to advotracker
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:48 +02:00
0582c1be43 frontend: policycheck_menu
* orbtk code to handle a menu structure

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
507f13ade3 frontend: examples: pdf_check example
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
2bc37f6a24 frontend: resources: used in example code
* example advotracker: stylesheet orbtk test widgets
* example advotracker: MaterialIcons ttf font

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
985cee4f0b frontend: locales: i18n updates
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
267c4faa2c frontend: function update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
22ecaaa0fe frontend: Cargo.toml: include csv for example csv-test
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
9c2beabd33 frontend: examples: remove old orbtk examples maintained upstream
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
53702555a3 Cargo.toml: set projects framework name
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
c3e939c6bb frontend: examples: introduce csv-test
* cli to test data import and export
  - import from a csv file
  - export to a csv file

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
dd8c840cf9 fontend: examples: .env for testing
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:47 +02:00
9e65ef198a frontend: example: csv-import helper update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
6a7d9a39d1 .gitignore: update new patterns
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
3b889d24e6 frontend: Node.toml
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
a6b6f84fec frontend: example: csv-import helper
* cli to import a given csv configured text file

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
de99076e53 frontend: Cargo.toml: new dependencies
* commandline parsing: clap, viperus
* csv file handling
* envirenment variable: dotenv, envy
* i18n: locales
* serde json support
* tracing: tracing, tracing-subscriber

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
8a8941f0cf policylist: wip: view and state handling updates
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
29b3876264 policydata: wip: view and state handling updates
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
62c786b671 policycheck: wip: view and state handling updates
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
717dc9ba03 frontend: update main_view
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
d4ae415ece frontend: main: update stylesheet reference, basic geometry
* new module 'policycheck_menu'
* reference the project stylesheet
* update a default geometry

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:46 +02:00
9354ea29ab frontend: keys: update Classes, DCES entities, component values
* the elements are available as public advertised string values

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:14:39 +02:00
7600f1b8df frontend: base: update 'PROP_POLICY_LISTS'
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 14:00:01 +02:00
f3af573bd7 frontend: data: define export structure for csv files
* data need to be transfered as a csv-file attachment via email
* each row takes a semicolon separeted list of string fields
* this structure is defined as rust 'struct CSVExport'
* the struct CSVExport will reference to its child stuctures.

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 13:59:56 +02:00
e93c19aacc Cargo.toml: reference to orbtk's local repository
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 13:55:10 +02:00
930d916e3f frontend: resources: assets/resources used inside the frontend
* images: referenced image files
* fonts: rendering elements
* stylesheets: theme definition using CSS subsystem

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 13:55:09 +02:00
2159cc0c24 frontend: base_state: global methods implemented as a trait
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 13:55:09 +02:00
36e90081b5 frontend: keys: static variables used in themes
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 13:55:09 +02:00
a59d364b62 advotracker: delete unused lib definition
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-07-12 13:54:41 +02:00
142 changed files with 2535321 additions and 5262 deletions

17
.gitignore vendored
View File

@@ -1,10 +1,21 @@
# This file should only ignore things that are generated during a build, # This file should only ignore things that are generated during a build,
# generated by common IDEs, and optional files controlled by the user that # generated by common IDEs, and optional files controlled by the user that
# affect the build (such as config.toml). # affect the build (such as config.toml).
# Backup files created from Editors
.#* .#*
.DS_Store .DS_Store
**/*.rs.bk
Cargo.lock
**/*~ **/*~
/target/
# user adaptions and configs values
/config.toml /config.toml
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
***.rs.bk

9
.gitmodules vendored
View File

@@ -1,6 +1,9 @@
[submodule "database"]
path = database
url = ./database/
[submodule "backend"] [submodule "backend"]
path = backend path = backend
url = ./backend/ url = ./backend/
[submodule "advotracker-db"]
path = advotracker-db
url = https://gitea.networkx.de:50443/rzerres/advotracker-db
[submodule "advotrackerd"]
path = advotrackerd
url = https://gitea.networkx.de:50443/rzerres/advotrackerd

View File

@@ -1,10 +1,10 @@
[package] [package]
name = "advotracker" name = "advotracker-framework"
version = "0.1.0" version = "0.1.0"
authors = ["Ralf Zerres <ralf.zerres@networkx.de>"] authors = ["Ralf Zerres <ralf.zerres@networkx.de>"]
description = "Supports lawyers to capture relevant data encountered during an online legal advice\n" description = "Supports lawyers to capture relevant data encountered during an online legal advice\n"
repository = "https://github.com/rzerres/advotracker" repository = "https://gitea.networkx.de:50443/rzerres/advotracker"
homepage = "https://github.com/rzerres/advotracker" homepage = "https://gitea.networkx.de:50443/rzerres/advotracker"
documentation = "https://docs.rs/advotracker" documentation = "https://docs.rs/advotracker"
readme = "README.md" readme = "README.md"
license = "0BSD OR MIT" license = "0BSD OR MIT"
@@ -43,7 +43,8 @@ overflow-checks = true
[workspace] [workspace]
members = [ members = [
"backend", "advotrackerd",
"frontend", "advotracker-db",
"advotracker",
"advotracker_qml", "advotracker_qml",
] ]

147
README.md
View File

@@ -1,24 +1,145 @@
<!-- AdvoTracker.markdown --> <!-- AdvoTracker README.md -->
<!-- version: 1.1 --> <!-- version: 0.1.3 -->
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-generate-toc again --> <!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-generate-toc again -->
<!-- markdown-toc end --> <!-- markdown-toc end -->
# AdvoTracker - Hotline tool für Advokaten # ## About
AdvoTracker unterstützt Anwälte bei der Erfassung von Mandatsdaten zu einer Online-Beratung. `AdvoTracker` supports lawyers to capture relevant data encountered during an online
legal advice.
Das Tool wurde als multi-Pattform Anwendung konzipiert. The application components are implemented using the programming language *Rust*.
The graphical user interface (GUI) uses the class-lib *OrbTk*, which is natively
encoded in *Rust* as well.
Als Programmiersprache wird *Rust* verwendet. Die Implementierung der `AdvoTracker` can be deployed on every target operating system that is officialy
GUI Elemente erfolgt mit der Klassenbibliothek *OrbTk*. supported by the `OrbTK` toolkit. Currently included are:
Als Dokumentationsquellen wird auf die offiziellen Projektseiten [Rust Book][^1] und [OrbTk][^2] verwiesen. * macOS
* Linux
* openBSD
* Redox OS
* Web-Browser
* Windows
# Internationalisierung # Porting to and in activ development are:
In allen Dialogen und Meldungen erfolgt die Ausgabe entsprechend der Sprache, die als * Android
aktive Sprache in der Umgebungsvariable definiert ist (default: LANG = en_US.UTF8 ) ausgewählt. * iOS
* ubuntu Touch
Reference is made to the official project pages of [Rust Book][^1] and [OrbTk][^2]
as documentation sources.
## Internationalization
Dialogs and messages generated inside the application will respect the system language
of the active user. This is adaptable via the environment variable
(default: LANG = en_US.UTF8 ).
Within the source code you can find translated constants in the subdirectory 'locales'
(<src-root>/advotracker/src/locales/advotracker.json).
For every supported language-string, you will find a block starting with the
generic message code, followed by the target language translation. Translation lines
are identified by its ISO Code (e.g. de_DE.UTF8, es_ES.UTF8).
## Installation from source code
You can either download the `Advotracker` source code as a Zip-File or using `git`
from projects repositoy URL.
The source code is subdiveded in three submodules:
* advotracker
* advotrackerd
* advotracker-db
Currently the given WEB-URL certificate is signed by a Microsoft CA.
Since this CA can't be verified from offical CA-lists you have to proceed as follows:
```sh
$ cd <your_path>
$ git -c http.https://gitea.networkx.de:50443.sslverify=false \
clone --recurse-submodules --jobs=10 \
https://gitea.networkx.de:50443/rzerres/advotracker
```
To compile the target binary code, you have to use rusts toolchain.
If this isn't already installed on your development system, please go ahead
and either use the official supported installation tool from the rust communtiy
([rustup][^3]). As an alternitiv, you can download and install the compiler package
using your systems package manager.
### native operating system binaries
A binary compatible with your target operating system will be compiled
with the following command:
```sh
$ cd <advotracker_root>
$ cargo build --release
```
The toolchain also allows to cross-compile for other operating systems. Please
consult the rust documentation for further instructions.
### web-browser application
- **Prerequisits**
To run as a browser, electron or cordova app, you have to install cargo-node:
```sh
cargo install -f cargo-node
```
cargo-node will require a working version of `npm` (>=v6.9.0).
You may download [Node.js][^4], which includes npm as well.
- **Start as browser app**
You can run `Advotracker` as a web-browser app with the following call.
Once the app is running, you can connect to it opening the given URL
in you Web-Browser.
```sh
cargo node run --target browser --bin advotracker
```
- **Start as electron app**
```sh
cargo node run --target electron advotracker
```
- **Start as android app**
```sh
cargo node run --target android --bin advotracker
```
## Build and run documentation
You can build and view the latest documentation by executing the following command:
```sh
cargo doc --no-deps --open
```
This will render the web-pages, collecting the documentation paragraphs in the
source-code. When finished, it will open a new tab in your web browser
presenting the documentation.
## Installation
To install the compiled binary you can use rust package manager `cargo` as well.
```sh
cd <advotracker_root>
$ cargo install
```
In addition, you will need to copy the resource folder into your target directory.
This will provide the access to used fonts, images und stylesheets.
--- ---
@@ -28,7 +149,7 @@ This work is licensed under a [Creative Common License 4.0][License-CC_BY]
![Creative Common Logo][Logo-CC_BY] ![Creative Common Logo][Logo-CC_BY]
© 2020 Ralf Zerres, Networkx GmbH <EFBFBD> 2020 Ralf Zerres, Networkx GmbH
--- ---
@@ -36,3 +157,5 @@ Foodnotes
[^1]: Rust Book: https://doc.rust-lang.org/book/ [^1]: Rust Book: https://doc.rust-lang.org/book/
[^2]: OrbTK GUI: https://gitlab.redox-os.org/redox-os/orbtk [^2]: OrbTK GUI: https://gitlab.redox-os.org/redox-os/orbtk
[^3]: Rustup: https://rustup.rs/
[^4]: Node.js: https://nodejs.org/dist/v10.16.3/

1
advotracker-db Submodule

Submodule advotracker-db added at d29c747d7b

16
advotracker/.env Normal file
View File

@@ -0,0 +1,16 @@
###
# .env
# will overwrite standard environment viariables
###
# set the language variable used in test-runs
#test LANG=es-ES
#test LANG=it-IT
#test LANG=C
# set the language variable (overwriting standard Enviroment)
#TEST_LANG=de_DE
#TEST_LANG=C
#RUST_LOG=advotracker=trace
RUST_LOG=trace
#RUST_LOG=none
USERNAME=hiedemann
DB_DRIVER=sqlite

48
advotracker/Cargo.toml Normal file
View File

@@ -0,0 +1,48 @@
[package]
name = "advotracker"
version = "0.1.4"
authors = ["Ralf Zerres <ralf.zerres@networkx.de>"]
description = "Frontend component that supports lawyers to capture relevant data encountered during an online legal advice."
readme = "README.md"
license = "(0BSD OR MIT)"
edition = "2018"
default-run = "advotracker"
[target.x86_64-pc-windows-gnu]
linker = "/usr/bin/x86_64-w64-mingw32-gcc"
ar = "/usr/x86_64-w64-mingw32i/bin/ar"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winuser"] }
#diesel = { version = "1.4", features = [ "postgres", "sqlite" ] }
[dependencies]
async-stream = "~0.2"
chrono = { version = "~0.4.0", features = ["serde"] }
clap = { version = "~2.33", features = ["suggestions", "color"] }
csv = { version = "~1.1" }
dotenv = { version = "~0.15.0" }
envy = { version = "~0.4" }
lazy_static = { version = "~1.4.0" }
log = { version = "~0.4.8" }
locales = { version = "~0.1" }
#orbtk = { version = "~0.3.1-alpha4" }
orbtk = { git = "https://github.com/redox-os/orbtk.git", branch = "develop" }
#orbtk = { path = "../../orbtk" }
serde = { version = "~1.0", features = ["derive"] }
#tokio = { version = "~0.2", features = ["macros", "rt-threaded", "stream", "time"] }
tracing = { version = "~0.1" }
tracing-subscriber = { version = "~0.2.0", features = ["tracing-log"] }
viperus = { git = "https://github.com/maurocordioli/viperus", features = ["cache", "fmt-clap", "fmt-env", "global", "watch"] }
[features]
# no features by default
default = []
debug = ["orbtk/debug"]
light = []
[package.metadata.bundle]
name = "advotracker"
identifier = "nwx.advotracker"
short_description = "Online legal advice helper."
description = "Supports lawyers to capture relevant data encountered during an online legal advice.\n"

8
advotracker/Node.toml Normal file
View File

@@ -0,0 +1,8 @@
[[apps]]
name = "advotracker"
width = 580
height = 280
assets = "resources/advotracker"
[[apps.fonts]]
font_family = "MaterialIcons-Regular"
src = "resources/fonts/MaterialIcons.ttf"

View File

@@ -0,0 +1,47 @@
// *
// * advotracker - Hotline tackingtool for Advocats
// *
// * Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
// * SPDX-License-Identifier: (0BSD or MIT)
// */
// the language class
Dictionary (
// the map of active identifiers
// like a struct, but keys are also values instead of just beenig identifiers
words: {
// policycheck_view
"Validation policy number": "Prüfung Versicherungsnummer",
"Policy number": "Versicherungsnummer",
"Policy code": "ID der Vers.-Nummer",
"Checklist elements: ": "Prüflistenelemente: ",
"Check result": "Prüfungsergebnis",
"Importing data": "Importiere Datensätze",
"Processing time": "Bearbeitungszeit",
"Error:": "Fehler:",
"Reason": "Grund",
"Policy number is to long": "Die Nummer ist zu lang",
"Policy number is to short": "Die Nummer ist zu kurz",
"The given policy number is invalid": "Die Versicherungsnummer ist ungültig",
"The given policy number is valid": "Die Versicherungsnummer ist gültig",
"Only numbers are valid": "Nur Nummern sind zulässig",
"Account": "Benutzer",
"Toggle theme": "Thema wechseln",
"Quit": "Beenden",
// localization view
"Hello": "Hallo",
"User": "Anwender",
"Localization dialog": "Lokalisierungs-Dialog",
"German": "Deutsch",
"English": "Englisch",
// configuration view
"Configuration settings": "Konfigurationseinstellungen",
"Configuration file": "Konfigurationsdatei",
"Language Id": "Sprach-Id",
"Default theme": "Standard-Thema",
"load": "laden",
"save": "speichern"
}
)

View File

@@ -0,0 +1,16 @@
Theme (
styles: {
"button_single_content_segmdl2": (
base: "button_single_content",
properties: {
"icon font": "$SEGEO_ICON_FONT",
},
),
"container_master_detail": (
base: "container",
properties: {
"padding": 14,
},
),
}
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 220 B

View File

Before

Width:  |  Height:  |  Size: 123 B

After

Width:  |  Height:  |  Size: 123 B

View File

Before

Width:  |  Height:  |  Size: 123 B

After

Width:  |  Height:  |  Size: 123 B

View File

Before

Width:  |  Height:  |  Size: 289 B

After

Width:  |  Height:  |  Size: 289 B

View File

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 126 B

View File

Before

Width:  |  Height:  |  Size: 158 B

After

Width:  |  Height:  |  Size: 158 B

View File

Before

Width:  |  Height:  |  Size: 351 B

After

Width:  |  Height:  |  Size: 351 B

View File

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 130 B

View File

Before

Width:  |  Height:  |  Size: 193 B

After

Width:  |  Height:  |  Size: 193 B

View File

Before

Width:  |  Height:  |  Size: 452 B

After

Width:  |  Height:  |  Size: 452 B

View File

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 131 B

View File

Before

Width:  |  Height:  |  Size: 223 B

After

Width:  |  Height:  |  Size: 223 B

View File

@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="105mm"
height="74mm"
viewBox="0 0 105 74"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="Hiedemann_Logo.svg">
<title
id="title850">Hiedemann Rechtsanwälte</title>
<defs
id="defs2" />
<sodipodi:namedview
units="mm"
inkscape:snap-global="true"
inkscape:snap-grids="true"
inkscape:snap-nodes="true"
inkscape:window-maximized="1"
inkscape:window-y="27"
inkscape:window-x="0"
inkscape:window-height="1088"
inkscape:window-width="2048"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="285.11146"
inkscape:cx="343.98278"
inkscape:zoom="1.4564683"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Hiedemann Rechtsanwälte</dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Hiedemann Rechtsanwälte</dc:title>
</cc:Agent>
</dc:creator>
<dc:publisher>
<cc:Agent>
<dc:title>Networkx GmbH</dc:title>
</cc:Agent>
</dc:publisher>
</cc:Work>
</rdf:RDF>
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>Hiedemann Rechtsanwälte</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline"
inkscape:label="Rahmen"
id="layer2"
inkscape:groupmode="layer" />
<g
style="display:inline"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Logo">
<g
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="/data/development/advotracker/advotracker/resources/images/hiedemann_logo.png"
transform="matrix(0.5,0,0,0.5,26.483775,16.188821)"
id="g1005">
<g
id="g910"
style="display:inline"
transform="translate(-1.958558,18.772823)"
inkscape:export-filename="/data/development/advotracker/advotracker/resources/images/hiedemann_logo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<rect
ry="6.2927017"
rx="0.08973857"
y="1.2013354"
x="4.9724188"
height="24.806967"
width="99.907379"
id="rect852"
style="vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.0926207;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<g
id="g1098"
transform="matrix(1.3250965,0,0,1.3250965,-38.983201,-118.19402)"
inkscape:export-filename="/data/development/advotracker/advotracker/resources/images/hiedemann_logo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<text
transform="scale(0.95388348,1.0483461)"
id="text835"
y="108.37959"
x="36.210163"
style="font-size:8.71712px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:'FreeSans, Normal';letter-spacing:2.60261px;word-spacing:0px;writing-mode:lr-tb;fill:#c46069;fill-opacity:1;stroke:#000000;stroke-width:0.363212px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8.71712px;font-family:'Cyntho Pro';-inkscape-font-specification:'Cyntho Pro, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#c46069;fill-opacity:1;stroke:none;stroke-width:0.363212px"
y="108.37959"
x="36.210163"
id="tspan833"
sodipodi:role="line">HIEDE<tspan
id="tspan889"
style="letter-spacing:1.31762px">M</tspan><tspan
id="tspan883"
style="letter-spacing:1.31762px;stroke-width:0.363212px">A</tspan>NN</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.58611px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:'FreeSans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:3.43958px;word-spacing:0px;writing-mode:lr-tb;fill:#9d9b9b;fill-opacity:1;stroke:none;stroke-width:0.444725px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="40.402027"
y="104.09837"
id="text853"
transform="scale(0.86606999,1.1546411)"><tspan
sodipodi:role="line"
id="tspan851"
x="40.402027"
y="104.09837"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:'Cyntho Pro';-inkscape-font-specification:'Cyntho Pro';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#9d9b9b;fill-opacity:1;stroke:none;stroke-width:0.444725px"><tspan
id="tspan925"
style="letter-spacing:3.70417px">REC</tspan><tspan
style="letter-spacing:3.33375px"
id="tspan1083">HT</tspan><tspan
style="letter-spacing:2.92894px"
id="tspan1075">S</tspan><tspan
style="letter-spacing:3.23585px"
id="tspan1069">AN</tspan><tspan
id="tspan921"
style="letter-spacing:2.94746px">WÄLTE</tspan></tspan></text>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 768 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 455 KiB

After

Width:  |  Height:  |  Size: 455 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 237 KiB

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
Target: debug
[ralf@jacara-x1 frontend]$ time ../target/debug/examples/csv-import --importFile /home/ralf/projekte/Hiedemann/Allianz_Vorgaben_Policycheck/ERG.txt -v
Jun 27 02:43:48.886 TRACE csv-import: "Umgebungsvariablen prüfen" state="gestartet"
Jun 27 02:43:48.886 TRACE csv-import: environment="system" lang="de_DE.UTF-8"
Jun 27 02:43:48.887 DEBUG csv-import: missing value for field test_lang
Jun 27 02:43:48.887 TRACE csv-import: environment="envy" lang="de_DE.UTF-8"
Jun 27 02:43:48.887 TRACE csv-import: "Umgebungsvariablen prüfen" state="beendet"
Jun 27 02:43:48.887 TRACE csv-import: process="Programmargumente prüfen" state="gestartet"
RUST_LOG=None
Jun 27 02:43:48.887 TRACE csv_import::parse_args: verbose 1
config_file: "csv_import.ron"
import_file: "/home/ralf/projekte/Hiedemann/Allianz_Vorgaben_Policycheck/ERG.txt"
verbosity level: 1
Jun 27 02:43:48.887 TRACE csv-import: process="Programmargumente prüfen" state="beendet"
Jun 27 02:43:48.887 TRACE csv-import: process="Programmlogik starten" state="gestartet"
Jun 27 02:43:48.887 TRACE csv-import: extension=Some("txt") file=File { fd: 3, path: "/home/ralf/projekte/Hiedemann/Allianz_Vorgaben_Policycheck/ERG.txt", read: true, write: false }
Jun 27 02:43:48.888 TRACE csv-import: header=StringRecord(["DION", "VERS", "POLLFNR"])
Jun 27 02:43:57.513 TRACE csv-import: record_count=2498330
Imported 2498330 records
Jun 27 02:43:57.513 TRACE csv-import: process="Programmlogik beendet" state="beendet"
real 0m8,700s
user 0m8,675s
sys 0m0,017s
Target: release
[ralf@jacara-x1 frontend]$ time ../target/release/examples/csv-import --importFile /home/ralf/projekte/Hiedemann/Allianz_Vorgaben_Policycheck/ERG.txt -v
Jun 27 02:48:13.753 TRACE csv-import: "Umgebungsvariablen prüfen" state="gestartet"
Jun 27 02:48:13.753 TRACE csv-import: environment="system" lang="de_DE.UTF-8"
Jun 27 02:48:13.753 DEBUG csv-import: missing value for field test_lang
Jun 27 02:48:13.753 TRACE csv-import: environment="envy" lang="de_DE.UTF-8"
Jun 27 02:48:13.753 TRACE csv-import: "Umgebungsvariablen prüfen" state="beendet"
Jun 27 02:48:13.753 TRACE csv-import: process="Programmargumente prüfen" state="gestartet"
RUST_LOG=None
Jun 27 02:48:13.753 TRACE csv_import::parse_args: verbose 1
config_file: "csv_import.ron"
import_file: "/home/ralf/projekte/Hiedemann/Allianz_Vorgaben_Policycheck/ERG.txt"
verbosity level: 1
Jun 27 02:48:13.753 TRACE csv-import: process="Programmargumente prüfen" state="beendet"
Jun 27 02:48:13.753 TRACE csv-import: process="Programmlogik starten" state="gestartet"
Jun 27 02:48:13.754 TRACE csv-import: extension=Some("txt") file=File { fd: 3, path: "/home/ralf/projekte/Hiedemann/Allianz_Vorgaben_Policycheck/ERG.txt", read: true, write: false }
Jun 27 02:48:13.754 TRACE csv-import: header=StringRecord(["DION", "VERS", "POLLFNR"])
Jun 27 02:48:14.098 TRACE csv-import: record_count=2498330
Imported 2498330 records
Jun 27 02:48:14.098 TRACE csv-import: process="Programmlogik beendet" state="beendet"
real 0m0,355s
user 0m0,338s
sys 0m0,017s

View File

@@ -0,0 +1,7 @@
# set the language variable used in test-runs
#test lang=it
TEST_LANG=en
#RUST_LOG=csv-import=trace
LOG=debug
USERNAME=ralf
DB_DRIVER=sqlite

View File

@@ -11,6 +11,7 @@ enum Action {
ValueChanged(Entity), ValueChanged(Entity),
IncrementCounter, IncrementCounter,
RemoveItem, RemoveItem,
ToggleTheme(Entity),
} }
#[derive(AsAny)] #[derive(AsAny)]
@@ -31,64 +32,72 @@ impl MainViewState {
} }
impl State for MainViewState { impl State for MainViewState {
fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) { fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
if let Some(action) = self.action { if let Some(action) = self.action {
match action { match action {
Action::AddItem => { Action::AddItem => {
let len = ctx.widget().get::<List>("list").len(); let len = main_view(ctx.widget()).list().len();
if len < 5 { if len < 5 {
ctx.widget() main_view(ctx.widget())
.get_mut::<List>("list") .list_mut()
.push(format!("Item {}", len + 1)); .push(format!("Item {}", len + 1));
ctx.child("items").set("count", len + 1); ctx.child("items").clone_or_default::<usize>("Item");
ctx.child("remove-item-button").set("enabled", true); items_widget(ctx.child("items")).set_count(len + 1);
button(ctx.child("remove-item-button")).set_enabled(true);
button(ctx.child("remove-item-button")).set_visibility(Visibility::Visible);
if len == 4 { if len == 4 {
ctx.child("add-item-button").set("enabled", false); button(ctx.child("add-item-button")).set_enabled(false);
button(ctx.child("add-item-button"))
.set_visibility(Visibility::Collapsed);
} }
} }
} }
Action::RemoveItem => { Action::RemoveItem => {
let len = ctx.widget().get::<List>("list").len(); let len = main_view(ctx.widget()).list().len();
if len > 0 { if len > 0 {
ctx.widget().get_mut::<List>("list").remove(len - 1); main_view(ctx.widget()).list_mut().remove(len - 1);
ctx.child("items").set("count", len - 1); items_widget(ctx.child("items")).set_count(len - 1);
ctx.child("add-item-button").set("enabled", true); button(ctx.child("add-item-button")).set_enabled(true);
button(ctx.child("add-item-button")).set_visibility(Visibility::Visible);
if len == 1 { if len == 1 {
ctx.child("remove-item-button").set("enabled", false); button(ctx.child("remove-item-button")).set_enabled(false);
button(ctx.child("remove-item-button"))
.set_visibility(Visibility::Collapsed);
} }
} }
} }
Action::IncrementCounter => { Action::IncrementCounter => {
*ctx.widget().get_mut::<usize>("counter") += 1; *main_view(ctx.widget()).counter_mut() += 1;
let counter = *ctx.widget().get::<usize>("counter"); let counter = *main_view(ctx.widget()).counter();
ctx.widget().set( main_view(ctx.widget())
"result", .set_result(String16::from(format!("Button count: {}", counter)));
String16::from(format!("Button count: {}", counter)),
);
} }
Action::ClearText => { Action::ClearText => {
ctx.widget().set("text_one", String16::from("")); main_view(ctx.widget()).set_text_one(String16::default());
ctx.widget().set("text_two", String16::from("")); main_view(ctx.widget()).set_text_two(String16::default());
} }
Action::EntryActivated(entity) => { Action::EntryActivated(entity) => {
let mut widget = ctx.get_widget(entity); let mut text_box = text_box(ctx.get_widget(entity));
let text = widget.get_mut::<String16>("text"); let text = text_box.text_mut();
println!("submitting {}", text); println!("submitting {}", text);
text.clear(); text.clear();
} }
Action::EntryChanged(entity) => { Action::EntryChanged(entity) => {
let widget = ctx.get_widget(entity); println!("entry changed: {}", text_box(ctx.get_widget(entity)).text());
let text = widget.get::<String16>("text");
println!("entry changed: {}", text);
} }
Action::ValueChanged(entity) => { Action::ValueChanged(_entity) => {
let val = //println!("Slider value changed");
((*ctx.get_widget(entity).get::<f64>("val")).floor() as i32).to_string(); }
ctx.child("value_text").set("text", String16::from(val)); Action::ToggleTheme(entity) => {
let light = *ctx.get_widget(entity).get::<bool>("selected");
let theme = if light { light_theme() } else { dark_theme() };
ctx.switch_theme(theme);
} }
} }
@@ -96,24 +105,19 @@ impl State for MainViewState {
} }
} }
fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context<'_>) { fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context) {
let mut selection_string = "Selected:".to_string(); let mut selection_string = "Selected:".to_string();
for index in &ctx.widget().get::<SelectedIndices>("selected_indices").0 { for index in &main_view(ctx.widget()).selected_indices().0 {
selection_string = format!("{} {}", selection_string, index); selection_string = format!("{} {}", selection_string, index);
} }
ctx.child("selection") text_block(ctx.child("selection")).set_text(selection_string);
.set("text", String16::from(selection_string));
} }
} }
fn create_header(ctx: &mut BuildContext, text: &str) -> Entity { fn create_header(ctx: &mut BuildContext, text: &str) -> Entity {
TextBlock::new() TextBlock::new().text(text).style("header").build(ctx)
.text(text)
.element("text-block")
.class("h1")
.build(ctx)
} }
type List = Vec<String>; type List = Vec<String>;
@@ -136,6 +140,14 @@ widget!(
impl Template for MainView { impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self { fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
let slider = Slider::new()
.min(0.0)
.max(1.0)
.on_changed(move |states, entity| {
state(id, states).action(Action::ValueChanged(entity));
})
.build(ctx);
self.name("MainView") self.name("MainView")
.result("Button count: 0") .result("Button count: 0")
.counter(0) .counter(0)
@@ -147,16 +159,16 @@ impl Template for MainView {
]) ])
.list_count(3) .list_count(3)
.selection_list(vec![ .selection_list(vec![
"Item 1".to_string(), "Select Item 1".to_string(),
"Item 2".to_string(), "Select Item 2".to_string(),
"Item 3".to_string(), "Select Item 3".to_string(),
"Item 4".to_string(), "Select Item 4".to_string(),
"Item 5".to_string(), "Select Item 5".to_string(),
"Item 6".to_string(), "Select Item 6".to_string(),
"Item 7".to_string(), "Select Item 7".to_string(),
"Item 8".to_string(), "Select Item 8".to_string(),
"Item 9".to_string(), "Select Item 9".to_string(),
"Item 10".to_string(), "Select Item 10".to_string(),
]) ])
.combo_box_list(vec![ .combo_box_list(vec![
"CB 1".to_string(), "CB 1".to_string(),
@@ -174,25 +186,21 @@ impl Template for MainView {
.combo_box_list_count(10) .combo_box_list_count(10)
.child( .child(
Grid::new() Grid::new()
.margin(8.0) .margin(8)
.columns( .columns(Columns::new().add(132).add(16).add(132).add(16).add(132))
Columns::new() .rows(Rows::new().add("*").add(32))
.add(132.0)
.add(16.0)
.add(132.0)
.add(16.0)
.add(132.0),
)
.child( .child(
// Row 0
Stack::new() Stack::new()
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::row(0))
// Column 0 // Column 0
.child(create_header(ctx, "Buttons")) .child(create_header(ctx, "Buttons"))
.child( .child(
Button::new() Button::new()
.text("Button") .text("Button")
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.icon(material_font_icons::CHECK_FONT_ICON) .icon(material_icons_font::MD_CHECK)
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::row(1)) .attach(Grid::row(1))
.on_click(move |states, _| { .on_click(move |states, _| {
@@ -204,19 +212,19 @@ impl Template for MainView {
.child( .child(
Button::new() Button::new()
.text("Primary") .text("Primary")
.element("button") .style("button_primary")
.class("primary") .margin((0, 8, 0, 0))
.margin((0.0, 8.0, 0.0, 0.0)) .icon(material_icons_font::MD_360)
.icon(material_font_icons::CHECK_FONT_ICON)
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::row(2)) .attach(Grid::row(2))
.build(ctx), .build(ctx),
) )
.child( .child(
ToggleButton::new() ToggleButton::new()
.class("single_content") .style("button_single_content")
.text("ToggleButton") .text("ToggleButton")
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 2, 0))
.icon(material_icons_font::MD_ALARM_ON)
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::row(3)) .attach(Grid::row(3))
.build(ctx), .build(ctx),
@@ -224,45 +232,38 @@ impl Template for MainView {
.child( .child(
CheckBox::new() CheckBox::new()
.text("CheckBox") .text("CheckBox")
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::row(4)) .attach(Grid::row(4))
.build(ctx), .build(ctx),
) )
.child( .child(
Switch::new() Switch::new()
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::row(5)) .attach(Grid::row(5))
.build(ctx), .build(ctx),
) )
.child(slider)
.child( .child(
TextBlock::new() ProgressBar::new()
.margin((0.0, 8.0, 0.0, 0.0)) .val(slider)
.element("h1") .margin((0, 8, 0, 0))
.id("value_text")
.text("0")
.h_align("center")
.build(ctx),
)
.child(
Slider::new()
.on_changed(move |states, entity| {
state(id, states).action(Action::ValueChanged(entity));
})
.build(ctx), .build(ctx),
) )
.build(ctx), .build(ctx),
) )
.child( .child(
Stack::new() Stack::new()
// Column 2
.attach(Grid::column(2)) .attach(Grid::column(2))
.attach(Grid::row(0))
.child(create_header(ctx, "Text")) .child(create_header(ctx, "Text"))
.child( .child(
TextBlock::new() TextBlock::new()
.class("body") .style("body")
.text(("result", id)) .text(("result", id))
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.attach(Grid::column(2)) .attach(Grid::column(2))
.attach(Grid::row(1)) .attach(Grid::row(1))
.build(ctx), .build(ctx),
@@ -271,7 +272,7 @@ impl Template for MainView {
TextBox::new() TextBox::new()
.water_mark("TextBox...") .water_mark("TextBox...")
.text(("text_one", id)) .text(("text_one", id))
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.attach(Grid::column(2)) .attach(Grid::column(2))
.attach(Grid::row(2)) .attach(Grid::row(2))
.on_activate(move |states, entity| { .on_activate(move |states, entity| {
@@ -286,7 +287,7 @@ impl Template for MainView {
TextBox::new() TextBox::new()
.water_mark("TextBox...") .water_mark("TextBox...")
.text(("text_two", id)) .text(("text_two", id))
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.attach(Grid::column(2)) .attach(Grid::column(2))
.attach(Grid::row(2)) .attach(Grid::row(2))
.on_activate(move |states, entity| { .on_activate(move |states, entity| {
@@ -299,9 +300,10 @@ impl Template for MainView {
) )
.child( .child(
Button::new() Button::new()
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.class("single_content") .style("button_single_content")
.text("clear text") .text("clear text")
.icon(material_icons_font::MD_CLEAR)
.on_click(move |states, _| { .on_click(move |states, _| {
state(id, states).action(Action::ClearText); state(id, states).action(Action::ClearText);
true true
@@ -310,8 +312,8 @@ impl Template for MainView {
) )
.child( .child(
NumericBox::new() NumericBox::new()
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.max(123.0) .max(123)
.step(0.123) .step(0.123)
.val(0.123) .val(0.123)
.build(ctx), .build(ctx),
@@ -319,24 +321,25 @@ impl Template for MainView {
.build(ctx), .build(ctx),
) )
.child( .child(
// Column 4
Grid::new() Grid::new()
.rows( .rows(
Rows::new() Rows::new()
.add("auto") .add("auto")
.add(32.0) .add(32)
.add(16.0) .add(16)
.add(204.0) .add(204)
.add("auto") .add("auto")
.add(192.0) .add(192)
.add("auto"), .add("auto"),
) )
.columns(Columns::new().add("*").add(4.0).add("*")) .columns(Columns::new().add("*").add(4).add("*"))
.attach(Grid::column(4)) .attach(Grid::column(4))
.attach(Grid::row(0))
.child( .child(
TextBlock::new() TextBlock::new()
.text("Items") .text("Items")
.element("text-block") .style("header")
.class("h1")
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::column_span(3)) .attach(Grid::column_span(3))
.attach(Grid::row(0)) .attach(Grid::row(0))
@@ -350,7 +353,8 @@ impl Template for MainView {
.get::<Vec<String>>("combo_box_list")[index] .get::<Vec<String>>("combo_box_list")[index]
.clone(); .clone();
TextBlock::new() TextBlock::new()
.margin((0.0, 0.0, 0.0, 2.0)) .style("small_text")
.margin((0, 0, 0, 2))
.v_align("center") .v_align("center")
.text(text) .text(text)
.build(bc) .build(bc)
@@ -359,58 +363,53 @@ impl Template for MainView {
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::column_span(3)) .attach(Grid::column_span(3))
.attach(Grid::row(1)) .attach(Grid::row(1))
.margin((0.0, 8.0, 0.0, 0.0)) .margin((0, 8, 0, 0))
.count(("combo_box_list_count", id)) .count(("combo_box_list_count", id))
.build(ctx), .build(ctx),
) )
.child( .child(
ItemsWidget::new() ItemsWidget::new()
.element("items-widget")
.id("items") .id("items")
.padding((4.0, 4.0, 4.0, 2.0)) .padding((4, 4, 4, 2))
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::column_span(3)) .attach(Grid::column_span(3))
.attach(Grid::row(3)) .attach(Grid::row(3))
.margin((0.0, 0.0, 0.0, 8.0)) .margin((0, 0, 0, 8))
// bc = build-context
.items_builder(move |bc, index| { .items_builder(move |bc, index| {
let text = bc.get_widget(id).get::<Vec<String>>("list") let text = bc.get_widget(id).get::<Vec<String>>("list")
[index] [index]
.clone(); .clone();
Button::new() Button::new().margin((0, 0, 0, 2)).text(text).build(bc)
.margin((0.0, 0.0, 0.0, 2.0))
.text(text)
.build(bc)
}) })
.count(("list_count", id)) .count(("list_count", id))
.build(ctx), .build(ctx),
) )
.child( .child(
Button::new() Button::new()
.element("button") .style("button_single_content")
.class("single_content")
.id("remove-item-button") .id("remove-item-button")
.icon(material_font_icons::MINUS_FONT_ICON) .icon(material_icons_font::MD_REMOVE_CIRCLE)
.on_click(move |states, _| { .on_click(move |states, _| {
state(id, states).action(Action::RemoveItem); state(id, states).action(Action::RemoveItem);
true true
}) })
.min_width(0.0) .min_width(0)
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::row(4)) .attach(Grid::row(4))
.build(ctx), .build(ctx),
) )
.child( .child(
Button::new() Button::new()
.element("button") .style("button_single_content")
.class("single_content")
.id("add-item-button") .id("add-item-button")
.icon(material_font_icons::ADD_FONT_ICON) .icon(material_icons_font::MD_ADD_CIRCLE)
.on_click(move |states, _| { .on_click(move |states, _| {
state(id, states).action(Action::AddItem); state(id, states).action(Action::AddItem);
true true
}) })
.min_width(0.0) .min_width(0)
.attach(Grid::column(2)) .attach(Grid::column(2))
.attach(Grid::row(4)) .attach(Grid::row(4))
.build(ctx), .build(ctx),
@@ -421,27 +420,28 @@ impl Template for MainView {
.attach(Grid::column_span(3)) .attach(Grid::column_span(3))
.attach(Grid::row(5)) .attach(Grid::row(5))
.selected_indices(id) .selected_indices(id)
.margin((0.0, 16.0, 0.0, 8.0)) .margin((0, 16, 0, 8))
.items_builder(move |bc, index| { .items_builder(move |bc, index| {
let text = bc let text = bc
.get_widget(id) .get_widget(id)
.get::<Vec<String>>("selection_list")[index] .get::<Vec<String>>("selection_list")[index]
.clone(); .clone();
TextBlock::new() TextBlock::new()
.margin((0.0, 0.0, 0.0, 2.0)) .margin((0, 0, 0, 2))
.v_align("center") .v_align("center")
.text(text) .text(text)
.build(bc) .build(bc)
}) })
.on_selection_changed(|_, _, _| println!("Selection changed"))
.count(("selection_list_count", id)) .count(("selection_list_count", id))
.build(ctx), .build(ctx),
) )
.child( .child(
// todo: wrong text width???? // todo: wrong text width????
TextBlock::new() TextBlock::new()
.element("text-block") .style("body")
.id("selection") .id("selection")
.max_width(120.0) .max_width(120)
.attach(Grid::column(0)) .attach(Grid::column(0))
.attach(Grid::column_span(3)) .attach(Grid::column_span(3))
.attach(Grid::row(6)) .attach(Grid::row(6))
@@ -450,6 +450,30 @@ impl Template for MainView {
) )
.build(ctx), .build(ctx),
) )
.child(
// Row 1
Stack::new()
.orientation("horizontal")
.attach(Grid::row(1))
.attach(Grid::column(0))
.child(
TextBlock::new()
.style("body")
.text("Toggle theme: ")
.v_align("center")
.margin((0, 0, 4, 0))
.build(ctx),
)
.child(
Switch::new()
.on_changed(move |states, entity| {
state(id, states).action(Action::ToggleTheme(entity));
})
.v_align("center")
.build(ctx),
)
.build(ctx),
)
.build(ctx), .build(ctx),
) )
} }
@@ -463,8 +487,8 @@ fn main() {
.window(|ctx| { .window(|ctx| {
Window::new() Window::new()
.title("OrbTk - widgets example") .title("OrbTk - widgets example")
.position((100.0, 100.0)) .position((100, 100))
.size(468.0, 730.0) .size(468, 730)
.resizeable(true) .resizeable(true)
.child(MainView::new().build(ctx)) .child(MainView::new().build(ctx))
.build(ctx) .build(ctx)

View File

@@ -0,0 +1,124 @@
use std::collections::HashMap;
//use std::sync::mpsc;
use orbtk::prelude::*;
use orbtk::shell::WindowRequest;
static ID_CHECK_POLICY_NUMBER: &'static str = "ID_CHECK_POLICY_NUMBER";
static ID_PROGRESS_BAR: &'static str = "ID_PROGRESS_BAR";
enum Action {
ParsePolicyNumber
}
#[derive(Default, AsAny)]
struct MainViewState {
action: Option<Action>,
progress_bar: Entity,
text_box: Entity,
progress_counter: f64
//records: HashMap::<String, String>,
//record_counter: u64
}
impl State for MainViewState {
fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
self.text_box = ctx.entity_of_child(ID_CHECK_POLICY_NUMBER).expect("Cannot get TextBox!");
self.progress_bar = ctx.entity_of_child(ID_PROGRESS_BAR).expect("Cannot get progress bar !");
}
fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
// if there is an action, process it
if let Some(action) = &self.action {
match action {
Action::ParsePolicyNumber => {
let value_to_parse = ctx.get_widget(self.text_box).get::<String16>("text").clone();
self.parse_policy_number(value_to_parse, ctx);
}
}
// Reset action
self.action = None;
}
}
}
impl MainViewState {
fn action(&mut self, action: Action) {
self.action = Some(action);
}
fn parse_policy_number(&mut self, _value: String16, ctx: &mut Context) {
self.import_csv(ctx);
}
fn import_csv(&mut self, ctx: &mut Context) {
// code to import csv file into a hashmap
// will read in number_of_records = 100%
// progress_counter should be incremented, if
// read_in_loop will reach next 10% -> self.progress_counter += 0.1
// now fire an event to update the widget
// question: how to fire up the event inside import_csv function,
// without the need to mute "ID_CHECK_POLICY_NUMBER" ?
// given code just increments, if you change "ID_CHECK_POLICY_NUMBER"
self.progress_counter += 0.1;
self.update_progress(ctx);
}
fn update_progress(&self, ctx: &mut Context) {
// create a mpsc::Sender<WindowRequest> object
let sender = ctx.window_sender();
let mut pgbar = ctx.get_widget(self.progress_bar);
pgbar.set::<f64>("val", self.progress_counter);
// redraw screen if sender has changed
// only way to trigger a redraw: create an event
sender.send(WindowRequest::Redraw).unwrap()
}
}
widget!(MainView<MainViewState>);
impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
self
.margin(32.0)
.child(
Stack::new()
.orientation("vertical")
.h_align("center")
.v_align("top")
.spacing(8.0)
.child(
TextBox::new()
.id(ID_CHECK_POLICY_NUMBER)
.water_mark("Mut value and type <Return>")
.on_activate(move |states, _entity| {
// you have to fire a new event to be able to get in the update() with access to Context
states.get_mut::<MainViewState>(id).action(Action::ParsePolicyNumber);
})
.build(ctx)
)
.child(
ProgressBar::new()
.id(ID_PROGRESS_BAR)
.build(ctx)
)
.build(ctx)
)
}
}
fn main() {
Application::new()
.window(|ctx| {
Window::new()
.title("incroment_progress_bar skeleton")
.position((100.0, 100.0))
.size(420.0, 730.0)
.resizeable(true)
.child(MainView::new().build(ctx))
.build(ctx)
})
.run();
}

View File

@@ -0,0 +1,7 @@
# set the language variable used in test-runs
#test lang=de
TEST_LANG=en
#RUST_LOG=csv-test=trace
RUST_LOG=trace
USERNAME=ralf
DB_DRIVER=sqlite

View File

@@ -0,0 +1,23 @@
DION VERS POLLFNR
1 AS 1
1 AS 11
1 AS 123456789
9 AS 99
9 AS 999
1 AS 1511111111
1 AS 1511111119
1 AS 9000000001
1 AS 9000000002
1 AS 9999999991
1 AS 9999999992
1 AS 9999999999
3 AS 1515735920
3 AS 9999999991
3 AS 9999999992
3 AS 9999999999
9 AS 9927764110
9 AS 9927764251
9 AS 9927764426
9 AS 9999999991
9 AS 9999999992
9 AS 9999999999

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
../../../advotrackerd/data/POLLFNR_WOECHENTLICH.txt

View File

@@ -1,7 +1,7 @@
{ {
"err.lang.not_found": { "err.lang.not_found": {
"de_DE.UTF-8": "Konnte Sprachkode nicht auslesen", "de_DE.UTF-8": "Konnte die Umgebungsvarialbe LANG nicht auslesen",
"de": "Konnte Sprachkode nicht auslesen", "de": "Konnte die Umgebungsvarialbe LANG nicht auslesen",
"en": "Couldn't read LANG" "en": "Couldn't read LANG"
}, },
"err.user.not_found": { "err.user.not_found": {
@@ -30,6 +30,11 @@
"de": "Umgebungsvariablen prüfen", "de": "Umgebungsvariablen prüfen",
"en": "Parsing environment" "en": "Parsing environment"
}, },
"parse.results": {
"de_DE.UTF-8": "Ergebnisse der Konfigurations-Parameterprüfung",
"de": "Ergebnisse der Konfigurationsparameterprüfung",
"en": "Config parsing results"
},
"config.name": { "config.name": {
"de_DE.UTF-8": "Konfigurationswert für", "de_DE.UTF-8": "Konfigurationswert für",
"de": "Konfigurationswert für", "de": "Konfigurationswert für",
@@ -60,6 +65,26 @@
"de": "Datenbank-Treiber", "de": "Datenbank-Treiber",
"en": "database driver" "en": "database driver"
}, },
"csv_import.started": {
"de_DE.UTF-8": "importieren von einer csv-datei gestartet",
"de": "importieren von einer csv-datei gestartet",
"en": "import from a csv-file started"
},
"csv_import.finished": {
"de_DE.UTF-8": "importieren von einer csv-datei beendet",
"de": "importieren von einer csv-datei beendet",
"en": "import from a csv-file finished"
},
"csv_export.started": {
"de_DE.UTF-8": "exportieren in eine csv-datei gestartet",
"de": "exportieren in eine csv-datei gestartet",
"en": "export to csv-file started"
},
"csv_export.finished": {
"de_DE.UTF-8": "exportieren in eine csv-datei beendet",
"de": "exportieren in eine csv-datei beendet",
"en": "export to csv-file finished"
},
"state.started": { "state.started": {
"de_DE.UTF-8": "gestartet", "de_DE.UTF-8": "gestartet",
"de": "gestartet", "de": "gestartet",

View File

@@ -0,0 +1,396 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use chrono::{Local, DateTime};
use locales::t;
//use serde::{Deserialize, Serialize};
use serde::Deserialize;
use std::env;
use std::{error::Error, process};
use std::collections::HashMap;
use tracing::{debug, trace, Level};
use advotracker::data::structures::{PolicyCode, PolicyList, PolicyDataList, PolicyData};
// include modules
mod parse_args;
/// respect environment variables set in .env files
/// located in the current call directory
/// this is primarily used in testing scenarios (eg. debugging)
#[derive(Debug, Deserialize)]
struct Environment {
test_lang: String,
log: String,
}
/// export as csv format
/// https://docs.rs/csv/1.1.3/csv/cookbook/index.html
/// https://blog.burntsushi.net/csv/
fn export(p: &mut String, lang: &String) -> Result<u64, Box<dyn Error>> {
use std::fs::File;
use std::path::Path;
//use std::ffi::OsStr;
use std::io::prelude::*;
let mut res = t!("csv.export.started", lang);
let mut state = t!("state.started", lang);
let dt_start: DateTime<Local> = Local::now();
trace!(target: "csv-test",
process = ?res,
state = ?state,
date_start = ?dt_start.to_string());
// Note: slash syntax also works on Windows!
let path = Path::new(p);
// only create files with a '.txt' extensions
//let extension = path.extension();
// match extension {
// //Some(String) => println!("file extension ok!"),
// //_ => extension = OsStr::new("txt")
// _ => println!("got file extension {:?}", extension)
// };
// open the file descriptor
let mut file = File::create(path)?;
trace!(target: "csv-export", extension = ?path.extension(), file = ?file);
// Build the CSV writer and push selected records.
//for result in csv_reader.records() {
let mut count = 0;
file.write_all(b"Allianz DirectCall Protokoll!")?;
count += 1;
let dt_end: DateTime<Local> = Local::now();
let duration = dt_end.signed_duration_since(dt_start);
println!("Duration: {:#?}", duration);
trace!(target: "csv-test", record_count = ?count, duration = ?duration);
state = t!("state.finished", lang);
res = t!("csv.import.finished", lang);
trace!(target: "csv-test", process = ?res, state = ?state);
Ok(count)
}
/// import from csv format
/// https://docs.rs/csv/1.1.3/csv/cookbook/index.html
/// https://blog.burntsushi.net/csv/
fn import(p: &mut String, data_list: &mut PolicyDataList,
policy_numbers: &mut HashMap<u64, PolicyCode>, lang: &String)
-> Result<u64, Box<dyn Error>> {
use std::fs::File;
use std::path::Path;
use std::ffi::OsStr;
let mut res = t!("csv.import.started", lang);
let mut state = t!("state.started", lang);
let dt_start: DateTime<Local> = Local::now();
trace!(target: "csv-test",
process = ?res,
state = ?state,
date_start = ?dt_start.to_string());
// Note: slash syntax also workd on Windows!
let path = Path::new(p);
// must be a readable file
trace!(target: "csv-test", path = ?path);
let valid = path.is_file();
println!("is_file: {}", valid);
//if let Some(res) = valid
assert_eq!(path.is_file(), true);
// only accept files with '.txt' extensions
let extension = path.extension();
assert_eq!(extension, Some(OsStr::new("txt")));
// open the file
let file = File::open(path)?;
trace!(target: "csv-test",
extension = ?extension,
file = ?file,
data_list = ?data_list.name);
// Build the CSV reader and iterate over each record.
let mut csv_reader = csv::ReaderBuilder::new()
.has_headers(true)
.delimiter(b' ')
.flexible(true)
//.comment(Some(b'#'))
//.from_reader(io::stdin());
//.from_path(path);
.from_reader(file);
{
// We nest this call in its own scope because of lifetimes.
let headers = csv_reader.headers()?;
trace!(target: "csv-test", header = ?headers);
}
// Deserialize the input data and push result to target vector
let mut count = 0;
for result in csv_reader.deserialize() {
// The iterator yields Result<StringRecord, Error>, so we check the
// error here.
let record: PolicyData = result?;
//println!("{:?}", record);
// WIP: write to redis backend
// append the policy_number to the HashMap
policy_numbers.insert(record.policy_number, record.policy_code);
// push record as new vector elements
data_list.push(record);
count +=1;
}
let dt_end: DateTime<Local> = Local::now();
let duration = dt_end.signed_duration_since(dt_start);
trace!(target: "csv-test", record_count = ?count, duration = ?duration);
state = t!("state.finished", lang);
res = t!("csv.import.finished", lang);
trace!(target: "csv-test",
process = ?res,
state = ?state,
date_stop = ?dt_end.to_string());
Ok(count)
}
#[allow(dead_code)]
/// validate a given policy number
/// result will return true or false
fn is_valid(policy_number: &u64, policy_list: &PolicyDataList,
policy_numbers: &mut HashMap<u64, PolicyCode>, lang: &String)
-> Result<bool, Box<dyn std::error::Error>> {
let mut res = t!("policy.validation.started", lang);
let mut state = t!("state.started", lang);
let dt_start: DateTime<Local> = Local::now();
trace!(target: "csv-test",
process = ?res,
state = ?state,
policy_number = ?policy_number,
policy_list = ?policy_list.name,
elements = ?policy_list.policy_data.len(),
date_start = ?dt_start.to_string());
//println!("Policy_number list: {:?}", policy_list.policy_data);
// WIP: testcode to collect policy_number via iterator
//println!("policy_number: {:?}", policy_list.policy_data[1].policy_number);
//println!("Policy_number list: {:?}", policy_list.policy_data);
// println!("policy_list: {:?} (with {:?} elements)",
// policy_list.name, policy_list.policy_data.len());
// policy_list.into_iter()
// .filter(|num| matches(w, w1))
// .clone
// .collect::<Vec<policy_number>>()
// let my_num = policy_list.policy_data.iter()
// .map(|policy_number| {
// policy_number
// })
// .collect::<Vec<_>>();
//println!("My policy_numbers: {:?}", my_num);
// let mut my_policy_list = [
// [ ("Jack", 20), ("Jane", 23), ("Jill", 18), ("John", 19), ],
// [ ("Bill", 17), ("Brenda", 16), ("Brad", 18), ("Barbara", 17), ]
// ];
// let teams_in_score_order = teams
// .iter_mut()
// .map(|team| {
// team.sort_by(|&a, &b| a.1.cmp(&b.1).reverse());
// team
// })
// .collect::<Vec<_>>();
//println!("Teams: {:?}", teams_in_score_order);
// if policy_list.policy_data.iter().any(|v| v == policy_number) {
// println!("{:?} contains {}", policy_list.name, policy_number);
// } else {
// println!("{:?} doesn't contain {}", policy_list, policy_number);
//}
// let test: Vec<_> = vec!["one", "two", "three"];
// let index: usize = test.iter().enumerate().find(|&r| r.1.to_string() == "two".to_string()).unwrap().0;
// println!("index: {:?} -> {:?}", index, test[index]);
//let index: usize = test.iter().enumerate().find(|&r| r.policy_number == "two".to_string()).unwrap().0;
let mut result = false;
match policy_numbers.get(&policy_number) {
Some(&policy_code) => {
let res = t!("policy.validation.success", lang);
println!("policy_number: {} ({:?})",
policy_number, policy_code);
result = true;
trace!(target: "csv-test",
policy_number = ?policy_number,
validation = ?res,
policy_code = ?policy_code);
},
_ => {
let res = t!("policy.validation.failed", lang);
//println!("Noop! Number isn't valid!");
println!("{:?}", res);
trace!(target: "csv-test",
policy_number = ?policy_number,
validation = ?res);
},
}
let dt_end: DateTime<Local> = Local::now();
let duration = dt_end.signed_duration_since(dt_start);
res = t!("policy.validation.finished", lang);
state = t!("state.finished", lang);
trace!(target: "csv-test",
process = ?res,
state = ?state,
date_stop = ?dt_end.to_string(),
duration = ?duration);
Ok(result)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
use dotenv::dotenv;
use parse_args::parse_args;
//use std::process;
//use std::sync::Arc;
use tracing_subscriber::fmt;
use viperus::Viperus;
//static DEFAULT_FILTER: &str = concat!(module_path!(), "=", "trace");
// initialize the tracing subsystem
// a drop in replacement for classical logging
// reference: https://tokio.rs/blog/2019-08-tracing/
let span = tracing::span!(Level::TRACE, "csv-test");
let _enter = span.enter();
let subscriber = fmt::Subscriber::builder()
.with_env_filter("trace")
//.with_max_level(tracing::Level::DEBUG)
.finish();
tracing::subscriber::with_default(subscriber, || {
// get system environment
let mut lang = env::var("LANG").unwrap_or("en".to_string());
let mut res = t!("parse.environment", lang);
let mut state = t!("state.started", lang);
trace!(target: "csv-test", message = ?res, state = ?state);
//debug!(message = ?res, state = ?state);
trace!(target: "csv-test", environment = "system", lang = ?lang);
// testing environment: read from .env file
dotenv().ok();
match envy::from_env::<Environment>() {
Ok(environment) => {
if environment.test_lang != lang { lang = environment.test_lang; }
},
Err(e) => { debug!(target: "csv-test", "{}", e); }
}
// how to handle unumplemented lang resources??
res = t!("parse.environment", lang);
trace!(target: "csv-test", environment = "envy", lang = ?lang);
state = t!("state.finished", lang);
trace!(target: "csv-test", message = ?res, state = ?state);
// initialize viperus structure
let mut v = Viperus::new();
// parse commandline arguments
res = t!("parse.arguments", lang);
state = t!("state.started", lang);
trace!(target: "csv-test", process = ?res, state = ?state);
let _ = parse_args(&mut v);
state = t!("state.finished", lang);
trace!(target: "csv-test", process = ?res, state = ?state);
//trace!(target: "Viperus", "Config results: {:?}", v);
// main tasks
res = t!("main.started", lang);
state = t!("state.started", lang);
trace!(target: "csv-test", process = ?res, state = ?state);
// importing policy code elements from csv-file
let policy_list = PolicyList::new("Allianz Versicherungsnummen-List");
println!("Policy List {:?} ", policy_list.name);
let mut policy_data = PolicyDataList::new("Allianz-Import 20200628");
println!("Policy Data List {:?} ", policy_data.name);
let mut policy_numbers : HashMap<u64, PolicyCode> = HashMap::new();
let mut csv_import_path = v.get::<String>("import_file").unwrap();
match import(&mut csv_import_path, &mut policy_data,
&mut policy_numbers, &lang) {
Ok(count) => {
println!("Imported {:?} records", count);
}
Err(err) => {
println!("error running Csv-Test: {}", err);
process::exit(1);
}
}
// test if policy_number is_valid
let test_policy_number = v.get::<i32>("test_policy_number").unwrap() as u64;
trace!(target: "csv-test", test_policy_number = ?test_policy_number);
//let policy_number : u64 = 1999999999;
//match is_valid(&policy_number, &policy_data, &mut policy_numbers, &lang) {
// Ok(true) => {
// use Hashmap method 'get' to check if we have the given key
match policy_numbers.get(&test_policy_number) {
Some(&policy_code) => {
let res = t!("policy.validation.success", lang);
println!("{:?}", res);
println!("policy_number: {} ({:?})",
test_policy_number, policy_code);
}
_ => {
let res = t!("policy.validation.failed", lang);
println!("{:?}", res);
//println!("Nuup! Number isn't valid!");
},
}
// export policy code elements to csv-file
let mut csv_export_path = v.get::<String>("export_file").unwrap();
match export(&mut csv_export_path, &lang) {
Ok(count) => {
println!("Exported {:?} records", count);
}
Err(err) => {
println!("error running CSV-Export: {}", err);
process::exit(1);
}
}
state = t!("state.finished", lang);
res = t!("main.finished", lang);
trace!(target: "csv-test", process = ?res, state = ?state);
});
Ok(())
}

View File

@@ -0,0 +1,216 @@
/*
* advotracker - Hotline tackingtool for Advocats
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
// parse CLI commandline arguments with clap
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
//use log::{debug, info, trace, warn};
use std::env;
use tracing::trace;
use viperus::Viperus;
/// Parse the commandline arguments and preset default values
/// Precedence: defaults -> config-file -> environment -> commandline
pub fn parse_args(v: &mut Viperus) -> Result<(), Box<dyn std::error::Error>> {
if cfg!(feature = "fmt-clap") {
trace!(target: "Viperus", "Viperus feature 'fmt-clap' enabled.");
println!("Using feature fmt-clap");
}
// preset default key/value pairs (lowest priority)
v.add_default("config_file", String::from("csv_import.ron"));
v.add_default("import_file", String::from("POLLFNR_WOECHENTLICH.txt"));
v.add_default("export_file", String::from(""));
v.add_default("test_policy_number", 123456789);
v.add_default("to_email_address_file", String::from("Allianz RA-Hotline <smr-rahotline@allianz.de>"));
v.add_default("from_email_address_file", String::from("Allianz-Hotline RA-Hiedemann <azhotline@hiedemann.de>"));
//v.add_default("username", String::from("nctalkbot"));
//v.add_default("password", String::from("botpassword"));
v.add_default("verbose", 0);
// CLI arguments are defined inline
let matches = App::new("csv-test")
.name(crate_name!())
.version(crate_version!())
.author(crate_authors!())
.about(crate_description!())
.after_help("
Testprogramm: Allianz Online-Beratung Im/Export CSV-Daten
Direct-Call via IVR-System (Interactive Voice Response)
SMR Deckungssummen-Prüfung: 089 92529 60211
SMR Unerledigt: 089 92529 60222")
.template(
"\
{bin} v{version}
{about}
{all-args}
(C) 2020 {author}
{after-help}",
)
.arg(
Arg::with_name("configFile")
.short("c")
.long("configFile")
.value_name("FILE")
.help("Select a config file")
.default_value("csv_import.ron")
.takes_value(true),
)
.arg(
Arg::with_name("importFile")
.short("i")
.long("importFile")
.help("Select source file for the csv-import")
.default_value("POLLFNR_WOECHENTLICH.txt")
//.default_value("POLLFNR_MINI.txt")
.takes_value(true),
)
.arg(
Arg::with_name("exportFile")
.short("e")
.long("exportFile")
.help("Select target file for the csv-export")
.default_value("RA-Hiedemann_DirectCall.txt")
.takes_value(true),
)
.arg(
Arg::with_name("fromEmailAddress")
.short("f")
.long("fromEmailAddress")
.help("Select the sender email-address (From:)")
.default_value("Allianz-Hotline RA-Hiedemann <azhotline@hiedemann.de>")
.takes_value(true),
)
.arg(
Arg::with_name("toEmailAddress")
.short("t")
.long("toEmailAddress")
.help("Select the target email-address (To:)")
.default_value("Allianz RA-Hotline <smr-rahotline@allianz.de>")
.takes_value(true),
)
.arg(
Arg::with_name("testPolicyNumber")
.short("p")
.long("testPolicyNumber")
.help("Test validity of given policy number")
//.default_value("")
.takes_value(true),
)
// .arg(
// Arg::with_name("username")
// .short("u")
// .long("username")
// .help("Sets username")
// .takes_value(true),
// )
// .arg(
// Arg::with_name("password")
// .short("P")
// .long("password")
// .help("Sets password")
// .takes_value(true),
// )
.arg(
Arg::with_name("verbose")
.short("v")
.long("verbose")
.help("Sets verbosity level")
.multiple(true),
)
.get_matches();
if matches.occurrences_of("verbose") > 0 {
// clap is using i64, viperus i32
let n = matches.occurrences_of("verbose") as i32;
v.add("verbose", n);
}
// preset the prefix for relevant environment variables ("ADVOTRACKER_")
let mut env_prefix: String = crate_name!().to_uppercase();
env_prefix.push_str("_");
v.set_env_prefix(&env_prefix);
// respect dotenv environment (e.g for testing)
// -> overwrites the preset default values
println!(
"RUST_LOG={}",
dotenv::var("RUST_LOG").unwrap_or_else(|_| String::from("None"))
);
// enable caching and automatic update of environment values
v.cache(true);
v.automatic_env(true);
// load user selected call arguments
// -> overwrites values given via environment variables
v.load_clap(matches)?;
// bond the clap names to camel_case rust variable names
v.bond_clap("configFile", "config_file");
v.bond_clap("importFile", "import_file");
v.bond_clap("exportFile", "export_file");
v.bond_clap("toEmailAddress", "to_email_address");
v.bond_clap("fromEmailAddress", "from_email_address");
v.bond_clap("testPolicyNumber", "test_policy_number");
//v.bond_clap("username", "username");
//v.bond_clap("password", "password");
v.bond_clap("verbose", "verbose");
trace!("verbose {:?}", v.get::<i32>("verbose").unwrap());
if v.get::<i32>("verbose").unwrap() > 0 {
println!(
"config_file: {:?}",
v.get::<String>("config_file").unwrap_or_default()
);
println!(
"import_file: {:?}",
v.get::<String>("import_file").unwrap_or_default()
);
println!(
"export_file: {:?}",
v.get::<String>("export_file").unwrap_or_default()
);
println!(
"to_email_address: {:?}",
v.get::<String>("to_email_address").unwrap_or_default()
);
println!(
"from_email_address: {:?}",
v.get::<String>("from_email_address").unwrap_or_default()
);
println!(
"test_policy_number: {:?}",
v.get::<i32>("test_policy_number").unwrap_or_default()
);
// println!(
// "username: {:?}",
// v.get::<String>("username").unwrap_or_default()
// );
// println!(
// "password: {:?}",
// v.get::<String>("password").unwrap_or_default()
// ); // only for testing now
println!(
"verbosity level: {:?}",
v.get::<i32>("verbose").unwrap_or_default()
);
}
if v.get::<i32>("verbose").unwrap() > 1 {
println!("\nEnvironment:");
for (key, value) in env::vars() {
println!("{}={}", key, value);
}
}
Ok(())
}

View File

@@ -0,0 +1,236 @@
use orbtk::prelude::*;
// use orbtk::theme::DEFAULT_THEME_CSS;
// use orbtk::theme::LIGHT_THEME_EXTENSION_CSS;
// //#[cfg(feature = "light-theme")]
// static WIDGET_EXT: &'static str = include_str!("../../resources/stylesheets/policyholder_check.css");
// fn get_theme() -> ThemeValue {
// //ThemeValue::create_from_css(LIGHT_THEME_EXTENSION_CSS)
// ThemeValue::create_from_css(DEFAULT_THEME_CSS)
// .extension_css(WIDGET_EXT)
// .build()
// }
#[derive(Debug, Copy, Clone)]
enum Action {
//ClearText,
EntryActivated(Entity),
EntryChanged(Entity),
//ValueChanged(Entity),
//ValueOk,
//ValueNone,
}
#[derive(AsAny)]
pub struct MainViewState {
action: Option<Action>,
}
impl Default for MainViewState {
fn default() -> Self {
MainViewState { action: None }
}
}
impl MainViewState {
fn action(&mut self, action: impl Into<Option<Action>>) {
self.action = action.into();
}
}
impl State for MainViewState {
fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
if let Some(action) = self.action {
match action {
// Action::ClearText => {
// ctx.widget().set("policynumber", String16::from(""));
// }
// Action::ValueOk => {
// //let mut text_box = TextBox::get(ctx.get_widget(entity));
// //let text = text_box.text_mut();
// //ctx.widget().set("policynumber", background("#4d4c4c"));
// }
// Action::ValueNone => {
// //let mut text_box = TextBox::get(ctx.get_widget(entity));
// //let text = text_box.text_mut();
// //ctx.widget().set("policynumber", background("#5b0f22"));
//}
Action::EntryActivated(entity) => {
let mut text_box = TextBox::get(ctx.get_widget(entity));
let text = text_box.text_mut();
//let mut widget = ctx.get_widget(entity);
//let text = widget.get_mut::<String16>("text");
println!("got value policynumber: {}", text);
//text.clear();
}
Action::EntryChanged(entity) => {
let widget = ctx.get_widget(entity);
let text = widget.get::<String16>("text");
println!("entry changed: {}", text);
}
// Action::ValueChanged(entity) => {
// let val =
// ((*ctx.get_widget(entity).get::<f64>("val")).floor() as i32).to_string();
// ctx.child("value_text").set("text", String16::from(val));
//}
}
self.action = None;
}
}
}
fn create_header(ctx: &mut BuildContext, text: &str) -> Entity {
TextBlock::new()
.text(text)
//.element("text-block")
.style("header")
.build(ctx)
}
widget!(
MainView<MainViewState> {
sum_policynumbers: usize,
policynumber: String16,
result: String16
}
);
impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
self.name("MainView").child(
//.result("Anzahl Versicherungsnummern: 0")
//.result("Policyholders count: 0").sum_policynumbers(0)
//.sum_policynumbers(0)
Grid::new()
.background("#fafafa")
//.width(200.0)
//.height(360.0)
.columns(
Columns::new()
.add(150.0)
.add("*")
//.add(120.0)
//.add("auto")
.add(150.0)
.build(),
)
.rows(
Rows::new()
.add("*")
.add("*")
.build(),
)
.child(
Grid::new()
//.element("policyholder_check")
.margin((4.0, 24.0, 24.0, 4.0))
.min_width(180.0)
.min_height(80.0)
.attach(Grid::column(1))
.attach(Grid::row(0))
.child(
Stack::new()
.spacing(8.0)
.orientation("vertical")
.h_align("center")
//.child(create_header(ctx, "Validation number policyholder"))
.child(create_header(ctx, "Validierung Versicherungsnummer"))
.child(
TextBox::new()
//.class("text_box")
// overwriting the class defaults
/* .background("transparent")
.foreground("#9dafbf")
.background("#fafafa")
/* .foreground(colors::LINK_WATER_COLOR) */
.border_brush("#5b0f22")
.border_width(5)
.border_radius(15) */
//.name("policynumber")
.focused(true)
.water_mark("Versicherungs-Nr...")
//.text("Number policyholder", id)
.text(("policynumber", id))
.font_size(24.0)
.h_align("stretch")
.on_activate(move |states, entity| {
state(id, states).action(Action::EntryActivated(entity));
})
.on_changed(move |states, entity| {
state(id, states).action(Action::EntryChanged(entity));
})
.build(ctx),
)
.child(
TextBlock::new()
//.element("text-block")
.id("result")
.min_width(80.0)
.max_width(180.0)
//.text("Result:")
.text("Ergebnis:")
.build(ctx),
)
.build(ctx),
)
.build(ctx)
)
.child(
Grid::new()
//.element("logo_customer")
.margin((9.0, 16.0, 16.0, 9.0))
.attach(Grid::column(0))
.attach(Grid::row(1))
.v_align("end")
.child(
ImageWidget::new()
.image("./resources/images/hiedemann_logo.png")
.build(ctx),
)
.build(ctx),
)
.child(
Grid::new()
//.element("logo_vendor")
.margin((9.0, 16.0, 16.0, 9.0))
.attach(Grid::column(2))
.attach(Grid::row(1))
.v_align("end")
.child(
ImageWidget::new()
.image("./resources/images/networkx_logo.png")
.build(ctx),
)
.build(ctx),
)
.build(ctx),
) // MainView
}
}
fn main() {
Application::new()
.window(|ctx| {
Window::new()
//.title("OrbTk - Policyholder checker example")
.name("MainWindow")
.title("AdvoTracker - Versicherungsnummern")
.position((-500.0, -100.0))
.size(480.0, 260.0)
.min_width(460.0)
.min_height(180.0)
.resizeable(true)
//.theme(get_theme())
.child(MainView::new().build(ctx))
.build(ctx)
})
.run();
}
// helper to request MainViewState
fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut MainViewState {
states.get_mut(id)
}

View File

@@ -0,0 +1 @@
../../resources

View File

@@ -0,0 +1,71 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
// Styles (RON based theme system)
pub static STYLE_BOTTOM_BAR: &'static str = "bottom_bar";
pub static STYLE_HEADER_BAR: &'static str = "header_bar";
pub static STYLE_SEPERATOR: &'static str = "seperator";
//pub static STYLE_ITEM_BUTTON: &'static str = "item_button";
pub static STYLE_MENU: &'static str = "menu";
pub static STYLE_BUTTON_MENU: &'static str = "button_menu";
pub static STYLE_STACK_MENU: &'static str = "stack_menu";
// Widget IDs (DCES: Entity[id] => [Component1, .. , Component<n>] -> data or state)
pub static ID_POLICY_CHECK_FORM: &'static str = "policy_check_form";
pub static ID_POLICY_CHECK_FORM_ROW_0: &'static str = "policy_check_form_row_0";
pub static ID_POLICY_CHECK_FORM_ROW_1: &'static str = "policy_check_form_row_1";
pub static ID_POLICY_CHECK_FORM_ROW_2: &'static str = "policy_check_form_row_2";
pub static ID_POLICY_CHECK_HEADER: &'static str = "policy_check_header";
pub static ID_POLICY_CHECK_ITEMS_WIDGET: &'static str = "policy_check_items_widget";
pub static ID_POLICY_CHECK_BUTTON_RESULT: &'static str = "policy_check_button_result";
pub static ID_POLICY_CHECK_BUTTON_MENU: &'static str = "policy_check_button_menu";
pub static ID_POLICY_CHECK_DATA_COUNT_BLOCK: &'static str = "policy_check_data_count_block";
pub static ID_POLICY_CHECK_HINT: &'static str = "policy_check_hint";
pub static ID_POLICY_CHECK_LABEL_HINT: &'static str = "policy_check_label_hint";
pub static ID_POLICY_CHECK_LABEL_MENU: &'static str = "policy_check_label_menu";
pub static ID_POLICY_CHECK_LABEL_POLICY_NUMBER: &'static str = "policy_check_label_policy_number";
pub static ID_POLICY_CHECK_LABEL_RESULT: &'static str = "policy_check_label_result";
pub static ID_POLICY_CHECK_MENU: &'static str = "policy_check_menu";
pub static ID_POLICY_CHECK_MENU_LABEL_ACCOUNT: &'static str = "policy_check_menu_label_account";
pub static ID_POLICY_CHECK_MENU_LABEL_QUIT: &'static str = "policy_check_menu_label_quit";
pub static ID_POLICY_CHECK_MENU_SHORTCUT_QUIT: &'static str = "policy_check_menu_shortcut_quit";
pub static ID_POLICY_CHECK_MENU_LABEL_TOGGLE_THEME: &'static str = "policy_check_menu_label_toggle_theme";
pub static ID_POLICY_CHECK_POLICY_NUMBER: &'static str = "policy_check_policy_number";
pub static ID_POLICY_CHECK_POPUP_MENU: &'static str = "policy_check_popup_menu";
pub static ID_POLICY_CHECK_POPUP_PROGRESS: &'static str = "policy_check_popup_progress";
pub static ID_POLICY_CHECK_PROGRESS_BAR: &'static str = "policy_check_progress_bar";
pub static ID_POLICY_CHECK_PROGRESS_TIME: &'static str = "policy_check_progress_time";
pub static ID_POLICY_CHECK_PROGRESS_TEXT: &'static str = "policy_check_progress_text";
pub static ID_POLICY_CHECK_RESULT: &'static str = "policy_check_result";
pub static ID_POLICY_CHECK_WIDGET: &'static str = "policy_check_widget";
pub static ID_POLICY_DATA_ADD_BUTTON: &'static str = "policy_data_add_button";
pub static ID_POLICY_DATA_COUNT: &'static str = "policy_data_count";
pub static ID_POLICY_DATA_LABEL: &'static str = "policy_data_label";
pub static ID_POLICY_DATA_ITEMS_WIDGET: &'static str = "policy_data_items_widget";
pub static ID_POLICY_DATA_DATE_INSERTED: &'static str = "policy_data_date_inserted";
pub static ID_POLICY_DATA_DION: &'static str = "policy_data_dion";
pub static ID_POLICY_DATA_POLICY_CODE: &'static str = "policy_data_policy_code";
pub static ID_POLICY_DATA_POLICY_NUMBER: &'static str = "policy_data_policy_number";
pub static ID_POLICY_DATA_STACK: &'static str = "policy_data_stack";
pub static ID_POLICY_DATA_STATUS: &'static str = "policy_data_status";
pub static ID_POLICY_DATA_LIST_NAME: &'static str = "policy_data_list_name";
pub static ID_POLICY_LIST_ADD_BUTTON: &'static str = "policy_list_add_button";
pub static ID_POLICY_LIST_ITEMS_WIDGET: &'static str = "policy_list_items_widget";
pub static ID_POLICY_LIST_TEXT_BOX: &'static str = "policy_list_text_box";
// Component Values (Properties)
pub static PROP_ADVOTRACKER: &'static str = "advotracker";
pub static PROP_POLICY_CHECK: &'static str = "policy_check";
pub static PROP_POLICY_PROGRESS_COUNT: &'static str = "policy_progress_count";
pub static PROP_POLICY_DATA_LIST: &'static str = "policy_data_list";
pub static PROP_POLICY_DATA_COUNT: &'static str = "policy_data_count";
pub static PROP_POLICY_LIST: &'static str = "policy_list";
pub static PROP_POLICY_LIST_COUNT: &'static str = "policy_list_count";

View File

@@ -0,0 +1,13 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
/// provides orbtk widgets constants
pub mod constants;
/// structures with implementations
pub mod structures;

View File

@@ -0,0 +1,345 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use chrono::NaiveDateTime;
use orbtk::prelude::*;
use serde::{Deserialize, Serialize};
/// An enumeration of valid policy codes.
/// right now, only "AS" is used.
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
pub enum PolicyCode {
/// Allianz Sachversicherung
AS
}
impl Default for PolicyCode {
fn default() -> Self { PolicyCode::AS }
}
/// Classifier of the Allinaz DirectCall communication type.
pub enum CommunicationType {
/// Anhörung
A,
/// Bußgeldbescheid
BU,
/// Gdb
Gdb,
/// Geburt
GE,
/// Hochzeit
HO,
/// Kündigung
KD,
/// Kaufvertrag
KV,
/// Lohn
LO,
/// Nachbar
NA,
/// Rente
RE,
/// Unfallflucht
UF,
/// Unterhalt
UH,
/// Unfall
UN,
/// Vodafone
VF,
/// Vermieter
VM,
/// Verwaltungsrecht
VW,
/// Zeugins
ZE,
/// Typ is unspecified
XX
}
impl Default for CommunicationType {
fn default() -> Self { CommunicationType::XX }
}
/// Status of a given policy data element.
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub enum Status {
/// Active -> the policy is active an supported
Active,
/// Inactive -> the policy is inactive or resigned
Inactive
}
impl Default for Status {
fn default() -> Self { Status::Active }
}
/// A communication type describes possible classifications of customer calls.
/// If the type isn't selected, the default will be 'unclassified -> XX'.
#[derive(Default, Deserialize, Serialize)]
pub struct CommunicationData {
/// pub communication_type: CommunicationType,
pub communication_type: String,
/// A literal name describing the selected communication type
pub communication_name: String
}
/// CSV Export
/// The structure elements are required for an export to a comma seperated text list.
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct CsvExportRecord {
/// Versicherungsschein Code
pub policy_code: String,
/// Versicherungsscheinnummer (10-stellig, numerisch)
pub policy_number: u64,
/// Anrufer: "Vorname, Nachname"
pub policy_holder: String,
/// Sachverhalt: "Kurzschilderung"
pub facts: String,
/// Schadensart: "harm_name (harm_id)"
pub harm_type: String,
/// communication_type => Kommunikationszeichen: "(communication_name)"
pub communication_name: String,
/// ra_hotline => RA_Hotline: Kanzlei Laqua, Kanzlei Buschbell, Kanzlei DAH, Kanzlei Hiedemann
pub ra_hotline: String, // const "Kanzlei Hiedemann",
/// ivr_comment => Haftungs-/Deckungskommentar; #IVR (ggf. ergänzt um das Beratungsergebnis)
pub ivr_comment: String,
}
/// Harm data are list/collections of harm types. You may toggle them to an unselected state.
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct HarmData {
/// Liste der Schadensdaten
pub harm_data: Vec<HarmType>,
/// Status der Schadenliste
//pub name: String,
pub selected: bool
}
/// Harm types are destincted by a type code.
/// The type code represents the unique harm identifier, bound with a literal name.
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct HarmType {
/// Kennzeichen des Schadenstyps
// Schadenersatz-RS im Verkehrsbereich (RS112)
// Ordnungswidrigkeits-RS im Verkehrsbereich (RS122)
// Straf-RS im Verkehrsbereich (RS121)
// Vertrags-RS im Verkehrsbereich (RS118)
// Vertrags-RS im Privatbereich (RS218)
// Arbeits-RS im Berufsbereich (RS315)
// WuG-RS im Privatbereich (RS220)
// Sozialgerichts-RS im Privatbereich (RS216)
// Rechtsschutz im Familien- und Erbrecht (RS217)
// Wagnis nicht versicherbar / versichert (RS999)
pub harm_type: String,
/// Beschreibung des Schadenstyps
pub harm_name: String,
}
/// Structure used to verify a policy data element.
#[derive(Default, Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct PolicyCheck {
/// Versicherungsschein-Prüfnummer
pub policy_check_number: String,
/// Referenz zum Versicherungsschein-Typ
pub dion: u8,
/// Referenz zum Versicherungsschein-Typ
pub policy_code: PolicyCode,
/// Referenz zur Versicherungsschein-Nummer
pub policy_number: u64,
/// Validitätsergebnis
pub policy_number_status: Status
}
impl PolicyCheck {}
// #[derive(Default, Clone, Debug, Serialize, Deserialize)]
// pub struct PolicyCheckList {
// pub title: String,
// pub list: Vec<PolicyCheck>
// }
// impl PolicyCheckList {
// pub fn new(title: impl Into<String>) -> Self {
// PolicyCheckList {
// title: title.into(),
// ..Default::default()
// }
// }
/// Structure collecting policy data elements
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct PolicyList {
/// Bescheibung der Versicherungsdatenliste
pub name: String,
/// Listenelemente der Versicherungsdatendaten
pub policy_list: Vec<PolicyDataList>,
}
/// implements helper methods, that manage lists of policy data collections
impl PolicyList {
/// Index auf Versicherungsliste zurückgeben (unveränderbar)
pub fn get(&self, index: usize) -> Option<&PolicyDataList> {
self.policy_list.get(index)
}
/// Index auf Versicherungsliste zurückgeben (veränderbar)
pub fn get_mut(&mut self, index: usize) -> Option<&mut PolicyDataList> {
self.policy_list.get_mut(index)
}
/// Neuer Eintrag am Anfang der Liste einfügen.
pub fn insert_front(&mut self, policy_list: PolicyDataList) {
self.policy_list.insert(0, policy_list);
}
/// Prüfung ob Versicherungsliste keine Elemente enthält.
pub fn is_empty(&self) -> bool {
self.policy_list.is_empty()
}
/// Berechnet die Anzahl der Versicherungslisten Elemente.
pub fn len(&self) -> usize {
self.policy_list.len()
}
/// Neuen Versicherungslisten-Namen erzeugen
pub fn new(name: impl Into<String>) -> Self {
PolicyList {
name: name.into(),
..Default::default()
}
}
/// Element der Versicherungsliste anfügen
pub fn push(&mut self, policy_list: PolicyDataList) {
self.policy_list.push(policy_list);
}
/// Element der Versicherungsliste löschen
pub fn remove(&mut self, index: usize) -> PolicyDataList {
self.policy_list.remove(index)
}
}
/// Structure collects policy data elements.
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct PolicyDataList {
/// Verfalldatum des Versicherungslisten Elements
pub date_valid_until: Option<NaiveDateTime>,
/// Elemente des Versicherungsobjektes
pub policy_data: Vec<PolicyData>,
/// Name der Versicherungsliste
pub name: String,
/// Status der Versicherungsliste
pub selected: bool
}
/// implements the helper methods, to manage policy data collections.
impl PolicyDataList {
/// Prüfung ob Versicherungselemente .
pub fn is_empty(&self) -> bool {
self.policy_data.is_empty()
}
/// Versicherungselement am Anfang anfügen
pub fn insert_front(&mut self, policy_data: PolicyData) {
self.policy_data.insert(0, policy_data);
}
/// Index zum Versicherungselement zurückgeben (unveränderbar)
pub fn get(&self, index: usize) -> Option<&PolicyData> {
self.policy_data.get(index)
}
/// Index zum Versicherungselement zurückgeben (veränderbar)
pub fn get_mut(&mut self, index: usize) -> Option<&mut PolicyData> {
self.policy_data.get_mut(index)
}
/// Berechnet die Anzahl der Versicherungselemente.
pub fn len(&self) -> usize {
self.policy_data.len()
}
/// Neues Versicherungselement erstellen.
pub fn new(name: impl Into<String>) -> Self {
// the new inserted element will be active by default
PolicyDataList {
date_valid_until: None,
name: name.into(),
selected: true,
..Default::default()
}
}
/// Versicherungselement anfügen
pub fn push(&mut self, policy_data: PolicyData) {
self.policy_data.push(policy_data);
}
/// Versicherungselement löschen
pub fn remove(&mut self, index: usize) -> PolicyData {
self.policy_data.remove(index)
}
}
/// Structure representing a policy data element
/// This structure groups a set of fields that describes a policy number.
/// The data are regularily updated via a data import from a comma seperated
/// text file (csv file).
/// Since we parse the source record fields to rust types (serde deserialize)
/// the field order in our struct must meet the data field order in the source!
/// We do use field attribute to precisely rename the source fields (header names)
/// to our target rust field names.
/// Referenz: POLLFNR_WOECHENTLICH.txt ->
/// Header 'DION VERS POLLFNR'
/// Record '1 AS 1515735810'
// DION: Allianz id => len = 1??
// VERS: policy_code => enum(AS; ??)
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
pub struct PolicyData {
/// Erstellungsdatum
pub date_inserted: Option<NaiveDateTime>,
/// Allianz Dion
#[serde(rename = "DION")]
pub dion: u8,
/// Kennzeichen des Allianz Versicherungscodes
#[serde(rename = "VERS")]
pub policy_code: PolicyCode,
/// Versicherungsscheinnummer (10stellig, numerisch)
#[serde(rename = "POLLFNR")]
pub policy_number: u64,
/// Status des Versicherungsscheins
pub status: Option<Status>
}
/// Policy Number Set
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct AllianzPolicyNumber {
/// Allianz Dion
#[serde(rename = "DION")]
pub dion: u8,
/// Kennzeichen des Allianz Versicherungstyps
#[serde(rename = "VERS")]
pub policy_code: String,
/// 10-stellige Versicherungsscheinnummer (numerisch)
#[serde(rename = "POLLFNR")]
pub policy_number: usize
}
// /// List of Allianz Policy Number records
// /// The structure elements are required for an export to a comma seperated text list.
// #[derive(Default, Debug, Deserialize)]
// pub struct AllianzPolicyNumberList {
// records: Vec<AllianzPolicyNumber>
// }
into_property_source!(PolicyCheck);
into_property_source!(PolicyDataList);
into_property_source!(PolicyList);

40
advotracker/src/lib.rs Normal file
View File

@@ -0,0 +1,40 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: 0BSD, MIT
*/
//#![warn(missing_docs, rust_2018_idioms, rust_2018_compatibility)]
#![warn(rust_2018_idioms, rust_2018_compatibility)]
//! advotracker
//! Supports lawyers to capture relevant data encountered during an
//! online legal advice.
//! This is the frontend componet that communicates to the backend `advotrackerd`.
//!
//! The crate is called `advotracker` and you can depend on it via cargo:
//!
//! ```ini
//! [dependencies]
//! advotracker = { version = "~0.1" }
//! ```
//!
//! WIP: provide a workflow image
//!
// /// The client specific services
// pub mod clients;
/// provides data definitions
pub mod data;
/// provides orbtk widgets (handling views and states)
pub mod widgets;
/// provides services
pub mod services;
// /// Provide test data
// #[cfg!Test]
// pub mod data;

View File

@@ -0,0 +1,238 @@
{
"err.lang.not_found": {
"de_DE.UTF-8": "Konnte Sprachkode nicht auslesen",
"de_DE": "Konnte Sprachkode nicht auslesen",
"C": "Couldn't read LANG"
},
"err.user.not_found": {
"fr": "Utilisateur introuvable: $email, $id",
"de_DE.UTF-8": "Anwender nicht gefunden: $email, $id",
"de_DE": "Anwender nicht gefunden: $email, $id",
"C": "User not found: $email, $id"
},
"main.started": {
"de_DE.UTF-8": "Programmlogik starten",
"de_DE": "Programmlogik starten",
"C": "Program logic started"
},
"main.finished": {
"de_DE.UTF-8": "Programmlogik beendet",
"de_DE": "Programmlogik beendet",
"C": "Program logic finished"
},
"parse.arguments": {
"de_DE.UTF-8": "Programmargumente prüfen",
"de_DE": "Programmargumente prüfen",
"C": "Parsing arguments"
},
"parse.environment": {
"de_DE.UTF-8": "Umgebungsvariablen prüfen",
"de_DE": "Umgebungsvariablen prüfen",
"C": "Parsing environment"
},
"parse.results": {
"de_DE.UTF-8": "Ergebnisse der Konfigurations-Parameterprüfung",
"de_DE": "Ergebnisse der Konfigurationsparameterprüfung",
"C": "Config parsing results"
},
"config.name": {
"de_DE.UTF-8": "Konfigurationswert für",
"de_DE": "Konfigurationswert für",
"C": "Config Value for"
},
"config.name.lang": {
"de_DE.UTF-8": "Sprach-Code_DE",
"de_DE": "Sprach-Code_DE",
"C": "Language code_DE"
},
"config.name.verbositylevel": {
"de_DE.UTF-8": "Ausgabe-Ebene",
"de_DE": "Ausgabe-Ebene",
"C": "verbosity level"
},
"config.name.environment": {
"de_DE.UTF-8": "Umgebungsvariablen",
"de_DE": "Umgebungsvariablen",
"C": "environment"
},
"config.name.configfile": {
"de_DE.UTF-8": "Konfigurations-Datei",
"de_DE": "Konfigurations-Datei",
"C": "config file"
},
"config.name.dbdriver": {
"de_DE.UTF-8": "Datenbank-Treiber",
"de_DE": "Datenbank-Treiber",
"C": "database driver"
},
"config.name.redis": {
"de_DE.UTF-8": "Redis Datenbank",
"de_DE": "Redis Datenbank",
"C": "Redis database"
},
"csv.export.started": {
"de_DE.UTF-8": "Export in eine CSV Datei starten",
"de_DE": "Export in eine CSV Datei starten",
"C": "Export to csv file started"
},
"csv.export.finished": {
"de_DE.UTF-8": "Export in eine CSV Datei beendet",
"de_DE": "Export in eine CSV Datei beendet",
"C": "Export to csv file finished"
},
"csv.import.started": {
"de_DE.UTF-8": "Import aus einer CSV Datei",
"de_DE": "Import aus einer CSV Datei",
"C": "Import from a csv file"
},
"csv.import.finished": {
"de_DE.UTF-8": "Import aus einer CSV Datei beendet",
"de_DE": "Import aus einer CSV Datei beendet",
"C": "Import from a csv file finished"
},
"redis.connection.error": {
"de_DE.UTF-8": "Es konnte keine Redis Verbindung aufgebaut werden",
"de_DE": "Es konnte keine Redis Verbindung aufgebaut werden",
"C": "Could not establish a redis connection"
},
"policy.hashmap.success": {
"de_DE.UTF-8": "Hashmap wurde erfolgreich erstellt",
"de_DE": "Hashmap wurde erfolgreich erstellt",
"C": "Hashmap creation was successfull"
},
"policy.hashmap.failed": {
"de_DE.UTF-8": "Hashmap konnte nicht erstellt werden",
"de_DE": "Hashmap konnte nicht erstellt werden",
"C": "Hashmap creation failed"
},
"policy.menu.label": {
"de_DE.UTF-8": "Menü",
"de_DE": "Menü",
"C": "Menu"
},
"policy.menu.label_account": {
"de_DE.UTF-8": "Aktueller Benutzer",
"de_DE": "Aktueller Benutzer",
"C": "Active user"
},
"policy.menu.label_quit": {
"de_DE.UTF-8": "Beenden",
"de_DE": "Beenden",
"C": "Quit"
},
"policy.menu.label_toggle_theme": {
"de_DE.UTF-8": "Thema wechseln",
"de_DE": "Thema wechseln",
"C": "Toggle theme"
},
"policy.string.header": {
"de_DE.UTF-8": "Validierung Versicherungsnummer",
"de_DE": "Validierung Versicherungsnummer",
"C": "Policy number validation"
},
"policy.string.label_policy_data": {
"de_DE.UTF-8": "Importierte Daten",
"de_DE": "Importierte Daten",
"C": "imported data"
},
"policy.string.label_policy_list": {
"de_DE.UTF-8": "Versicherungsnummern-Liste",
"de_DE": "Versicherungsnummern-Liste",
"C": "policy list"
},
"policy.string.label_policy_number": {
"de_DE.UTF-8": "Versicherungsnummer",
"de_DE": "Versicherungsnummer",
"C": "policy number"
},
"policy.string.label_result": {
"de_DE.UTF-8": "Prüfungsergebnis",
"de_DE": "Prüfungsergebnis",
"C": "check result"
},
"policy.string.data_count": {
"de_DE.UTF-8": "Anzahl Prüflistenelemente",
"de_DE": "Anzahl Prüflistenelemente",
"C": "Number of checklist elements"
},
"policy.string.import_data": {
"de_DE.UTF-8": "Importieren der Prüflisten-Elemente",
"de_DE": "Importieren der Prüflisten-Elemente",
"C": "Import checklist elements"
},
"policy.string.progress_time": {
"de_DE.UTF-8": "Bearbeitungszeit",
"de_DE": "Bearbeitungszeit",
"C": "processing time"
},
"policy.string.progress_text": {
"de_DE.UTF-8": "Importiere Daten",
"de_DE": "Importiere Daten",
"C": "Importing data"
},
"policy.validation.button_failed": {
"de_DE.UTF-8": "ungültig",
"de_DE": "ungültig",
"C": "invalid"
},
"policy.validation.button_success": {
"de_DE.UTF-8": "gültig",
"de_DE": "gültig",
"C": "valid"
},
"policy.validation.failed": {
"de_DE.UTF-8": "Die Versicherungsscheinnummber ist ungültig",
"de_DE": "Die Versicherungsscheinnummber ist ungültig",
"C": "The given policy number is invalid"
},
"policy.validation.new": {
"de_DE.UTF-8": "Neue Prüfung",
"de_DE": "Neue Prüfung",
"C": "New validation"
},
"policy.validation.success": {
"de_DE.UTF-8": "Die Versicherungsscheinnummber ist gültig",
"de_DE": "Die Versicherungsscheinnummber ist gültig",
"C": "The given policy number is valid"
},
"policy.validation.started": {
"de_DE.UTF-8": "Die Prüfung der Versicherungsscheinnummber wurde gestartet",
"de_DE": "Die Prüfung der Versicherungsscheinnummber wurde gestartet",
"C": "Validation of the policy number started"
},
"policy.validation.finished": {
"de_DE.UTF-8": "Die Prüfung der Versicherungsscheinnummber wurde beendet",
"de_DE": "Die Prüfung der Versicherungsscheinnummber wurde beendet",
"C": "Validation of the policy number finished"
},
"policy.validation.invalid_input": {
"de_DE.UTF-8": "Nur Nummer sind zulässig",
"de_DE": "Nur Nummer sind zulässig",
"C": "Only numbers are valid"
},
"policy.validation.not_found": {
"de_DE.UTF-8": "Nummer ist nicht aktiviert",
"de_DE": "Nummer ist nicht aktiviert",
"C": "number isn't marked active"
},
"policy.validation.to_long": {
"de_DE.UTF-8": "Die Nummer ist zu lang",
"de_DE": "Die Nummer ist zu lang",
"C": "Policy number is to long"
},
"policy.validation.to_short": {
"de_DE.UTF-8": "Die Nummer ist zu kurz",
"de_DE": "Die Nummer ist zu kurz",
"C": "Policy number is to short"
},
"state.started": {
"de_DE.UTF-8": "gestartet",
"de_DE": "gestartet",
"C": "started"
},
"state.finished": {
"de_DE.UTF-8": "beendet",
"de_DE": "beended",
"C": "finished"
}
}

182
advotracker/src/main.rs Normal file
View File

@@ -0,0 +1,182 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
// suppress creation of a new console window on window
#![windows_subsystem = "windows"]
//#[macro_use]
//extern crate lazy_static;
//use chrono::{Local, DateTime};
use dotenv::dotenv;
use locales::t;
use serde::Deserialize;
use std::env;
//use std::process;
use tracing::{info, trace, Level};
use orbtk::{
prelude::*,
};
// The Main view
use advotracker::{
widgets::{
main_view,
//policycheck::policycheck_state::PolicyCheckState,
}
};
mod parse_args;
/// define valid environment variables provided via .env files
/// located in the current call directory
/// this is primarily used in testing scenarios (eg. debugging)
#[derive(Debug, Deserialize)]
struct Environment {
lang: String,
test_lang: String,
rust_log: String,
}
// Style extension
//static FLUENT_DARK_EXT: &str = include_str!("../assets/advotracker/fluent_dark.ron");
//static FLUENT_LIGHT_EXT: &str = include_str!("../assets/advotracker/fluent_light.ron");
// German localization file.
static ADVOTRACKER_DE_DE: &str = include_str!("../assets/advotracker/advotracker_de_DE.ron");
/// Get the active language environment.
fn get_lang() -> String {
// get system environment
let mut lang = env::var("LANG").unwrap_or("C".to_string());
// testing environment: read from .env file
dotenv().ok();
match envy::from_env::<Environment>() {
Ok(environment) => {
if environment.test_lang != lang { lang = environment.test_lang; }
},
Err(e) => { info!(target: "advotracker", "{}", e) }
}
trace!(target: "advotracker", lang = ?lang);
lang
}
// /// Extend and register theme assets.
// fn theme() -> Theme {
// register_default_fonts(Theme::from_config(
// ThemeConfig::from(FLUENT_DARK_EXT)
// .extend(ThemeConfig::from(THEME_FLUENT))
// .extend(ThemeConfig::from(THEME_FLUENT_COLORS_DARK))
// .extend(ThemeConfig::from(THEME_FLUENT_FONTS)),
// // ThemeConfig::from(FLUENT_LIGHT_EXT)
// // .extend(ThemeConfig::from(THEME_FLUENT))
// // .extend(ThemeConfig::from(THEME_FLUENT_COLORS_DARK))
// // .extend(ThemeConfig::from(THEME_FLUENT_FONTS)),
// ))
// }
fn main() -> Result<(), Box<dyn std::error::Error>> {
use parse_args::parse_args;
use tracing_subscriber::fmt;
use viperus::Viperus;
let machine_kind = if cfg!(unix) {
"unix"
} else if cfg!(windows) {
"windows"
} else {
"unknown/unsupported"
};
// respect dotenv environment (e.g for testing)
// -> overwrites the preset default values
let rust_log = dotenv::var("RUST_LOG").unwrap_or_else(|_| String::from("None"));
// initialize the tracing subsystem
// a drop in replacement for classical logging
// reference: https://tokio.rs/blog/2019-08-tracing/
let span = tracing::span!(Level::TRACE, "advotracker");
let _enter = span.enter();
let subscriber = fmt::Subscriber::builder()
.with_env_filter(&rust_log)
//.with_max_level(tracing::Level::DEBUG)
.finish();
tracing::subscriber::with_default(subscriber, || {
// get system environment
let lang = get_lang();
let mut state = t!("state.started", lang);
let mut res = t!("parse.environment", lang);
trace!(target: "advotracker", message = ?res, state = ?state);
trace!(target: "advotracker", environment = "system", lang = ?lang);
trace!(target: "advotracker", machine = ?&machine_kind);
// how to handle unimplemented lang resources??
res = t!("parse.environment", lang);
trace!(target: "advotracker", environment = "envy", lang = ?lang);
state = t!("state.finished", lang);
trace!(target: "advotracker", message = ?res, state = ?state);
// initialize viperus structure
let mut viperus = Viperus::new();
// parse commandline arguments
res = t!("parse.arguments", lang);
state = t!("state.started", lang);
trace!(target: "advotracker", process = ?res, state = ?state);
let _ = parse_args(&mut viperus);
state = t!("state.finished", lang);
trace!(target: "advotracker", process = ?res, state = ?state);
// main tasks
res = t!("main.started", lang);
state = t!("state.started", lang);
trace!(target: "advotracker", process = ?res, state = ?state);
// use this only if you want to run it as web application.
orbtk::initialize();
// if no dictionary is set for the default language e.g. english the content of the text property will drawn.
let localization = RonLocalization::create()
.language("en_US")
.dictionary("de_DE", ADVOTRACKER_DE_DE)
.build();
Application::from_name("nwx.advotracker")
.localization(localization)
.window(|ctx| {
Window::new()
.title("AdvoTracker - DirectCall")
.position((500.0, 100.0))
.size(580.0, 320.0)
//.min_width(460.0)
//.min_height(180.0)
.resizeable(true)
.child(main_view::MainView::new().build(ctx))
.build(ctx)
})
.run();
state = t!("state.finished", lang);
res = t!("main.finished", lang);
trace!(target: "advotracker", process = ?res, state = ?state);
});
Ok(())
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View File

@@ -0,0 +1,213 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
// parse CLI commandline arguments with clap
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
//use log::{debug, info, trace, warn};
use std::env;
use tracing::trace;
use viperus::Viperus;
/// Parse the commandline arguments and preset default values
/// Precedence: defaults -> config-file -> environment -> commandline
pub fn parse_args(viperus: &mut Viperus) -> Result<(), Box<dyn std::error::Error>> {
if cfg!(feature = "global") {
trace!(target: "Viperus", "Viperus feature 'global' enabled.");
println!("Lasy static: 'VIPERUS' is accessible globaly");
}
if cfg!(feature = "fmt-clap") {
trace!(target: "Viperus", "Viperus feature 'fmt-clap' enabled.");
println!("Using feature fmt-clap");
}
// preset default key/value pairs (lowest priority)
viperus.add_default("config_file", String::from("csv_import.ron"));
viperus.add_default("import_file", String::from("POLLFNR_WOECHENTLICH.txt"));
viperus.add_default("export_file", String::from(""));
viperus.add_default("test_policy_number", 123456789);
viperus.add_default("to_email_address_file", String::from("Allianz RA-Hotline <smr-rahotline@allianz.de>"));
viperus.add_default("from_email_address_file", String::from("Allianz-Hotline RA-Hiedemann <azhotline@hiedemann.de>"));
//viperus.add_default("username", String::from("nctalkbot"));
//viperus.add_default("password", String::from("botpassword"));
viperus.add_default("verbose", 0);
// CLI arguments are defined inline
let matches = App::new("advotracker")
.name(crate_name!())
.version(crate_version!())
.author(crate_authors!())
.about(crate_description!())
.after_help("
Testprogramm: Allianz Online-Beratung Im/Export CSV-Daten
Direct-Call via IVR-System (Interactive Voice Response)
SMR Deckungssummen-Prüfung: 089 92529 60211
SMR Unerledigt: 089 92529 60222")
.template(
"\
{bin} v{version}
{about}
{all-args}
(C) 2020 {author}
{after-help}",
)
.arg(
Arg::with_name("configFile")
.short("c")
.long("configFile")
.value_name("FILE")
.help("Select a config file")
.default_value("csv_import.ron")
.takes_value(true),
)
.arg(
Arg::with_name("importFile")
.short("i")
.long("importFile")
.help("Select source file for the csv-import")
.default_value("POLLFNR_WOECHENTLICH.txt")
//.default_value("POLLFNR_MINI.txt")
.takes_value(true),
)
.arg(
Arg::with_name("exportFile")
.short("e")
.long("exportFile")
.help("Select target file for the csv-export")
.default_value("RA-Hiedemann_DirectCall.txt")
.takes_value(true),
)
.arg(
Arg::with_name("fromEmailAddress")
.short("f")
.long("fromEmailAddress")
.help("Select the sender email-address (From:)")
.default_value("Allianz-Hotline RA-Hiedemann <azhotline@hiedemann.de>")
.takes_value(true),
)
.arg(
Arg::with_name("toEmailAddress")
.short("t")
.long("toEmailAddress")
.help("Select the target email-address (To:)")
.default_value("Allianz RA-Hotline <smr-rahotline@allianz.de>")
.takes_value(true),
)
.arg(
Arg::with_name("testPolicyNumber")
.short("p")
.long("testPolicyNumber")
.help("Test validity of given policy number")
//.default_value("")
.takes_value(true),
)
// .arg(
// Arg::with_name("username")
// .short("u")
// .long("username")
// .help("Sets username")
// .takes_value(true),
// )
// .arg(
// Arg::with_name("password")
// .short("P")
// .long("password")
// .help("Sets password")
// .takes_value(true),
// )
.arg(
Arg::with_name("verbose")
.short("v")
.long("verbose")
.help("Sets verbosity level")
.multiple(true),
)
.get_matches();
if matches.occurrences_of("verbose") > 0 {
// clap is using i64, viperus i32
let n = matches.occurrences_of("verbose") as i32;
viperus.add("verbose", n);
}
// preset the prefix for relevant environment variables ("ADVOTRACKER_")
let mut env_prefix: String = crate_name!().to_uppercase();
env_prefix.push_str("_");
viperus.set_env_prefix(&env_prefix);
// enable caching and automatic update of environment values
viperus.cache(true);
viperus.automatic_env(true);
// load user selected call arguments
// -> overwrites values given via environment variables
viperus.load_clap(matches)?;
// bond the clap names to camel_case rust variable names
viperus.bond_clap("configFile", "config_file");
viperus.bond_clap("importFile", "import_file");
viperus.bond_clap("exportFile", "export_file");
viperus.bond_clap("toEmailAddress", "to_email_address");
viperus.bond_clap("fromEmailAddress", "from_email_address");
viperus.bond_clap("testPolicyNumber", "test_policy_number");
//viperus.bond_clap("username", "username");
//viperus.bond_clap("password", "password");
viperus.bond_clap("verbose", "verbose");
trace!("verbose {:?}", viperus.get::<i32>("verbose").unwrap());
if viperus.get::<i32>("verbose").unwrap() > 0 {
println!(
"config_file: {:?}",
viperus.get::<String>("config_file").unwrap_or_default()
);
println!(
"import_file: {:?}",
viperus.get::<String>("import_file").unwrap_or_default()
);
println!(
"export_file: {:?}",
viperus.get::<String>("export_file").unwrap_or_default()
);
println!(
"to_email_address: {:?}",
viperus.get::<String>("to_email_address").unwrap_or_default()
);
println!(
"from_email_address: {:?}",
viperus.get::<String>("from_email_address").unwrap_or_default()
);
println!(
"test_policy_number: {:?}",
viperus.get::<i32>("test_policy_number").unwrap_or_default()
);
// println!(
// "username: {:?}",
// viperus.get::<String>("username").unwrap_or_default()
// );
// println!(
// "password: {:?}",
// viperus.get::<String>("password").unwrap_or_default()
// ); // only for testing now
println!(
"verbosity level: {:?}",
viperus.get::<i32>("verbose").unwrap_or_default()
);
}
if viperus.get::<i32>("verbose").unwrap() > 1 {
println!("\nEnvironment:");
for (key, value) in env::vars() {
println!("{}={}", key, value);
}
}
Ok(())
}

View File

@@ -0,0 +1,41 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
//use chrono::{Local, DateTime};
use locales::t;
use std::error::Error;
//use std::collections::HashMap;
use tracing::trace;
//use crate::db::redis;
//use crate::data::structures::{PolicyCode, PolicyDataList, PolicyData};
/// export as csv format
/// https://docs.rs/csv/1.1.3/csv/cookbook/index.html
/// https://blog.burntsushi.net/csv/
pub fn export(p: &mut String, lang: &String) -> Result<(), Box<dyn Error>> {
use std::fs::File;
use std::path::Path;
//use std::ffi::OsStr;
let mut res = t!("csv.export.started", lang);
let mut state = t!("state.started", lang);
trace!(target: "csv-export", process = ?res, state = ?state);
// Note: slash syntax also works on Windows!
let path = Path::new(p);
// open the file
let file = File::open(path)?;
trace!(target: "csv.export", extension = ?path.extension(), file = ?file);
state = t!("state.finished", lang);
res = t!("csv.export.finished", lang);
trace!(target: "csv-export", process = ?res, state = ?state);
Ok(())
}

View File

@@ -0,0 +1,9 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
/// Exporting Allianz DirecCall data
pub mod allianzdirectcall;

View File

@@ -0,0 +1,133 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use chrono::{Local, DateTime};
use locales::t;
use std::error::Error;
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
use tracing::trace;
//use crate::db::redis;
use crate::data::structures::{PolicyCode, PolicyDataList, PolicyData};
//use crate::data::structures::PolicyDataList;
/// import AllianzDirectCall data from a csv delimeted file
/// save records to redis backend
/// https://docs.rs/csv/1.1.3/csv/cookbook/index.html
/// https://blog.burntsushi.net/csv/
pub fn import(p: &mut String, data_list: &mut PolicyDataList,
policy_numbers: &mut HashMap<u64, PolicyCode>,
policy_data_count: &mut u64,
lang: &String)
-> Result<(u64, Duration), Box<dyn Error>> {
use std::fs::File;
use std::path::Path;
use std::ffi::OsStr;
let mut res = t!("csv.import.started", lang);
let mut state = t!("state.started", lang);
let time_start = SystemTime::now();
let datetime: DateTime<Local> = time_start.into();
trace!(target: "csv-import", process = ?res, state = ?state, date_start = ?datetime.to_string());
// Note: slash syntax also works on Windows!
let path = Path::new(p);
// must be a readable file
trace!(target: "csv-import", path = ?path);
assert_eq!(path.is_file(), true);
// only accept files with '.txt' extensions
let extension = path.extension();
assert_eq!(extension, Some(OsStr::new("txt")));
// open the file
let file = File::open(path)?;
trace!(target: "csv-import", extension = ?extension, file = ?file);
// Build the CSV reader
let mut csv_reader = csv::ReaderBuilder::new()
.has_headers(true)
.delimiter(b' ')
//.comment(Some(b'#'))
//.flexible(true)
.from_reader(file);
{
// We nest this call in its own scope because of lifetimes.
let headers = csv_reader.headers()?;
trace!(target: "csv-import", header = ?headers);
}
// Iterate over each record, deserialize und write to our structures
let mut count : u64 = 0;
for result in csv_reader.deserialize() {
// The iterator yields Result<StringRecord, Error>, so we check the
// error here.
let record: PolicyData = result?;
//if verbose > 3 {
// println!("{:?}", record);
//}
// WIP: write to redis backend
// for now: append the policy_number to the HashMap
policy_numbers.insert(record.policy_number, record.policy_code);
// push record as new vector elements
data_list.push(record);
count +=1;
*policy_data_count = count;
};
let time_end = SystemTime::now();
let duration = time_end.duration_since(time_start)
.expect("Clock may have gone backwards");
trace!(target: "csv-import", record_count = ?count, duration = ?duration);
state = t!("state.finished", lang);
res = t!("csv.import.finished", lang);
let datetime: DateTime<Local> = time_end.into();
trace!(target: "csv-import", process = ?res, state = ?state, date_stop = ?datetime.to_string());
Ok((count, duration))
}
#[test]
fn test_import() {
// Takes a reference and returns Option<&V>
let my_policy_numbers : [u64; 2] = [1511111111, 9999999993];
assert_eq!(my_policy_numbers, [1511111111, 9999999993]);
let mut csv_import_path = String::from("data/POLLFNR_TEST.txt");
let mut policy_data = PolicyDataList::new("PolicyDataList");
let mut policy_numbers : HashMap<u64, PolicyCode> = HashMap::new();
let mut policy_data_count: u64 = 0;
let lang = "en".to_string();
match import(&mut csv_import_path, &mut policy_data,
&mut policy_numbers, &mut policy_data_count,
&lang) {
Ok((count, duration)) => {
println!("import {:?} records. Duration: {:?}", count, duration);
}
Err(err) => {
println!("error running CSV-Import: {}", err);
}
};
for p in &my_policy_numbers {
match policy_numbers.get(&p) {
Some(policy_code) => {
println!("Test: Policy-Number {:?} => Policy-Type {:?}, as expected!",
p, policy_code);
},
_ => println!("Test: Policy-Number {:?} => not valid, can't dereference the Policy-Type as expected", p),
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use std::error::Error;
use std::io;
use std::process;
/// Testing: import from csv format (Standard in)
fn import() -> Result<(), Box<dyn Error>> {
// Build the CSV reader and iterate over each record.
let mut csv_reader = csv::Reader::from_reader(io::stdin());
for result in csv_reader.records() {
// The iterator yields Result<StringRecord, Error>, so we check the
// error here.
let record = result?;
println!("{:?}", record);
}
Ok(())
}
fn main() {
if let Err(err) = import() {
println!("error running example: {}", err);
process::exit(1);
}
}

View File

@@ -0,0 +1,9 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
/// Importing Allianz DirecCall data
pub mod allianzdirectcall;

View File

@@ -0,0 +1,12 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
/// advotracker import modules
pub mod imports;
/// advotracker export modules
pub mod exports;

View File

@@ -0,0 +1,81 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use dotenv::dotenv;
use serde::Deserialize;
use std::env;
use tracing::{info, trace};
use orbtk::prelude::*;
use crate::{
data::constants::*,
data::structures::PolicyList
};
/// define valid environment variables provided via .env files
/// located in the current call directory
/// this is primarily used in testing scenarios (eg. debugging)
#[derive(Debug, Deserialize)]
struct Environment {
test_lang: String,
rust_log: String,
}
/// Provides generic methods to handle states of datatypes.
pub trait GlobalState {
/// Get the active language environment.
fn get_lang() -> String {
// get system environment
let mut lang = env::var("LANG").unwrap_or("C".to_string());
// testing environment: read from .env file
dotenv().ok();
match envy::from_env::<Environment>() {
Ok(environment) => {
if environment.test_lang != lang { lang = environment.test_lang; }
},
Err(e) => { info!(target: "advotracker", "{}", e) }
}
trace!(target: "advotracker", lang = ?lang);
lang
}
// /// Navigates to the given entity.
// fn navigate(&self, to: Entity, ctx: &mut Context<'_>) {
// if let Some(old_focused_element) = ctx.window().get::<Global>("global").focused_widget {
// let mut old_focused_element = ctx.get_widget(old_focused_element);
// old_focused_element.set("focused", false);
// //old_focused_element.update_theme_by_state(false);
// }
// ctx.window().get_mut::<Global>("global").focused_widget = None;
// ctx.widget().set("visibility", Visibility::Collapsed);
// ctx.get_widget(to).set("visibility", Visibility::Visible);
// }
/// Save the our data structure and convert it to `ron` file format.
/// The cargo package identifier (here: 'nwx.advotracker') is taken to create the app directory.
/// in users 'settings directory'. The directory location is OS dependant
/// (Windows: AppData, Unix: XDG_CONFIG_HOME, MacOS: $HOME/Library/Preferences).
/// The data is stored in filename PROP_ADVOTRACKER (here: `advotracker.ron`)
fn save(&self, registry: &mut Registry, ctx: &mut Context<'_>) {
registry
.get::<Settings>("settings")
.save(
PROP_ADVOTRACKER,
ctx.widget().get::<PolicyList>(PROP_ADVOTRACKER),
)
.unwrap();
}
}
// #[cfg(test)]
// mod tests {
// #[test]
// assert_eq!(2 + 2, 4);
// }

View File

@@ -0,0 +1,318 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use orbtk::prelude::*;
use crate::{
data::structures::PolicyCheck,
widgets::policycheck::policycheck_view::PolicycheckView,
};
type List = Vec<String>;
// [START] views
widget!(MainView {
// policy_list: PolicyList,
// policy_data_list: PolicyDataList,
// policylist_view: u32,
// policydata_view: u32,
policycheck_view: PolicyCheck
});
impl Template for MainView {
fn template(self, _id: Entity, ctx: &mut BuildContext<'_>) -> Self {
let policycheck_view = PolicycheckView::new()
//.policy_number_count(0)
//.policylist_view(id)
.build(ctx);
// let policylist_view = PolicyListView::new()
// .back_entity(policycheck_view.0)
// .policydata_view(id)
// .policylist_view(id)
// .policy_list_count(id)
// .build(ctx);
// let policydata_view = PolicyDataView::new()
// .back_entity(policylist_view.0)
// .policylist_view(id)
// .visibility("collapsed")
// .build(ctx);
self.name("MainView")
.policycheck_view(PolicyCheck::default())
// //.policycheck_view(0)
// .policydata_view(policydata_view.0)
// //.policylist_view(PolicyList::default())
// .policylist_view(policylist_view.0)
// .child(policydata_view)
// .child(policylist_view)
.child(
TabWidget::new()
.tab("Policynumber check", policycheck_view)
.tab("Localization", LocalizationView::new().build(ctx))
.tab("Configuration", ConfigurationView::new().build(ctx))
.build(ctx),
)
//.child(policycheck_view)
}
}
widget!(LocalizationView<LocalizationState> {
languages: List,
selected_index: i32 }
);
impl Template for LocalizationView {
fn template(self, id: Entity, ctx: &mut BuildContext<'_>) -> Self {
let languages = vec!["English".to_string(), "German".to_string()];
let count = languages.len();
self.languages(languages).selected_index(1).child(
Stack::new()
.width(120)
.margin(16)
.spacing(8)
.child(TextBlock::new().text("Hello").build(ctx))
.child(TextBlock::new().text("User").build(ctx))
.child(TextBlock::new().text("Localization dialog").build(ctx))
.child(
ComboBox::new()
.count(count)
.items_builder(move |bc, index| {
let text = bc.get_widget(id)
.get::<Vec<String>>("languages")[index]
.clone();
TextBlock::new().v_align("center").text(text).build(bc)
})
.on_changed("selected_index", move |states, _| {
states.get_mut::<LocalizationState>(id).change_language();
})
.selected_index(id)
.build(ctx),
)
.build(ctx),
)
}
}
widget!(
ConfigurationView<ConfigurationState> {
configuration_file: String,
language_id: String
}
);
impl Template for ConfigurationView {
fn template(self, id: Entity, ctx: &mut BuildContext<'_>) -> Self {
self.child(
Grid::new()
.margin(4)
.columns(
Columns::create()
.push(120)
.push(12)
.push("auto")
)
.rows(Rows::create()
.push("auto")
.push(4)
.push("auto")
.push(4)
.push("auto")
.push(12)
.push("auto"),
)
.child(
TextBlock::new()
.h_align("start")
.attach(Grid::column(0))
.attach(Grid::row(0))
.attach(Grid::column_span(3))
.text("Configuration settings")
.style("header")
.build(ctx),
)
.child(
TextBlock::new()
.attach(Grid::column(0))
.attach(Grid::row(2))
.v_align("center")
.h_align("end")
.text("Configuration file")
.build(ctx),
)
.child(
TextBox::new()
.attach(Grid::column(2))
.attach(Grid::row(2))
.text(("configuration_file", id))
.water_mark("Filename...")
.build(ctx),
)
.child(
TextBlock::new()
.attach(Grid::column(0))
.attach(Grid::row(4))
.v_align("center")
.h_align("end")
.text("Language Id")
.build(ctx),
)
.child(
TextBox::new()
.attach(Grid::column(2))
.attach(Grid::row(4))
.text(("language_id", id))
.water_mark("Locale Identifier...")
.build(ctx),
)
.child(
Grid::new()
.h_align("center")
.attach(Grid::column(0))
.attach(Grid::row(6))
.attach(Grid::column_span(3))
.columns(
Columns::create()
.push("auto")
.push(8)
.push("auto")
)
.rows(Rows::create()
.push("auto")
)
.child(
Button::new()
.style("button_single_content")
.attach(Grid::column(0))
.attach(Grid::row(0))
.text("load")
.on_click(move |ctx, _| {
ctx.send_message(ConfigurationAction::LoadConfiguration, id);
true
})
.build(ctx),
)
.child(
Button::new()
.text("save")
.style("button_single_content")
.attach(Grid::column(2))
.attach(Grid::row(0))
.on_click(move |ctx, _| {
ctx.send_message(ConfigurationAction::SaveConfiguration, id);
true
})
.build(ctx),
)
.build(ctx),
)
.build(ctx),
)
}
}
// [END] views
// [START] states
#[derive(AsAny, Debug, Default)]
struct LocalizationState {
change_language: bool,
}
impl LocalizationState {
fn change_language(&mut self) {
self.change_language = true;
}
}
impl State for LocalizationState {
fn update(&mut self, _registry: &mut Registry, ctx: &mut Context<'_>) {
if !self.change_language {
return;
}
let index = *LocalizationView::selected_index_ref(&ctx.widget()) as usize;
let selected_language = LocalizationView::languages_ref(&ctx.widget())[index].clone();
match selected_language.as_str() {
"English" => ctx.set_language("en_US"),
"German" => ctx.set_language("de_DE"),
_ => {}
}
self.change_language = false;
}
}
#[derive(Debug, Default, AsAny)]
struct ConfigurationState {}
impl State for ConfigurationState {
fn messages(
&mut self,
mut messages: MessageReader,
registry: &mut Registry,
ctx: &mut Context<'_>,
) {
for message in messages.read::<ConfigurationAction>() {
match message {
ConfigurationAction::LoadConfiguration => registry
.get::<Settings>("settings")
.load_async::<ConfigurationData>("configuration_data".to_string(), ctx.entity()),
ConfigurationAction::SaveConfiguration => {
let configuration_file: String = ConfigurationView::configuration_file_clone(&ctx.widget());
let language_id: String = ConfigurationView::language_id_clone(&ctx.widget());
registry.get::<Settings>("settings").save_async(
"configuration_data".to_string(),
ConfigurationData(
configuration_file,
language_id
),
ctx.entity(),
);
}
}
}
// save result
for message in messages.read::<SettingsResult<()>>() {
println!("Result {:?}", message);
}
// load result
for message in messages.read::<SettingsResult<ConfigurationData>>() {
if let Ok(data) = message {
ConfigurationView::configuration_file_set(&mut ctx.widget(), data.0);
ConfigurationView::language_id_set(&mut ctx.widget(), data.1);
}
}
}
}
// [END] states
// [START] Configuration data
#[derive(Clone, Debug)]
enum ConfigurationAction {
SaveConfiguration,
LoadConfiguration,
}
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct ConfigurationData(
pub String,
pub String
);
// [END] Configuration data

View File

@@ -0,0 +1,21 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
/// global helper functions
pub mod global_state;
/// The starting point (Main View)
pub mod main_view;
/// The policy check wigdet
pub mod policycheck;
// /// The policy data widget
// pub mod policydata;
// /// The policy lists widget
// pub mod policylist;

View File

@@ -0,0 +1,12 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
/// The policy check state
pub mod policycheck_state;
/// The policy check view
pub mod policycheck_view;

View File

@@ -0,0 +1,70 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use orbtk::prelude::*;
use crate::data::constants::*;
widget!(
PolicyCheckMenu {
is_active: Bool
}
);
impl Template for PolicyCheckMenu {
fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
// let policy_check_menu_container = Container::new()
// .id(ID_POLICY_CHECK_MENU_CONTAINER)
// .class(CLASS_MENU)
// .attach(Grid::row(3))
// .attach(Grid::column(8))
// .margin((0.0, 0.0, 4.0, 0.0))
// .enabled(false)
// .min_size(120.0, 180.0)
// .v_align("center")
// .on_click(move |ctx, _| {
// ctx.get_mut::<PolicyCheckState>(id)
// .action(Action::NewEntry(quit));
// true
// })
// .build(ctx);
// let policycheck_menu_container = Container::new()
// .id(ID_POLICY_CHECK_MENU_CONTAINER)
// .background("#dfebf5")
// .width(200.0)
// .height(200.0)
// .child(
// The menu implemented as an overlay
//ctx.append_child_to_overlay(policy_check_menu_button).unwrap();
self.name("PolicyCheckMenu").child(
Stack::new()
.id(ID_POLICY_CHECK_MENU)
.orientation("vertical")
.spacing(8)
.h_align("center")
.child(
Button::new()
.icon(material_icons_font::MD_VERIFIED_USER)
.text("angemeldeter Benutzer")
.margin((0, 8, 0, 0))
.build(ctx),
)
.child(
Button::new()
.icon(material_icons_font::MD_EXIT_TO_APP)
.text("Quit")
.margin((0, 8, 0, 0))
.build(ctx),
)
.build(ctx),
)
}
}

View File

@@ -0,0 +1,714 @@
use locales::t;
use orbtk::prelude::*;
use serde::Deserialize;
use std::process;
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
use tracing::{error, info, trace};
use crate::{
widgets::global_state::GlobalState,
data::{
structures::{PolicyCode, PolicyDataList, PolicyList},
constants::*,
},
services::imports::allianzdirectcall::import,
};
/// Actions that can execute on the task view.
#[derive(Debug, Clone, Copy)]
pub enum PolicycheckAction {
ClearEntry(Entity),
ChangeTheme(),
InputTextChanged(Entity),
ImportData,
OpenMenu(Entity),
ParseEntry(Entity),
AddProgress(f64),
ResetProgress,
SetMenu(Entity),
SetProgress(f64),
SetProgressPopup(Entity),
RemoveFocus(Entity),
RemoveMenu(Entity),
RemovePopup(Entity),
SetEntry(Entity),
SetVisibility(Entity),
TextChanged(Entity, usize),
ToggleTheme(Entity)
}
/// define valid environment variables provided via .env files
/// located in the current call directory
/// this is primarily used in testing scenarios (eg. debugging)
#[derive(Debug, Deserialize)]
struct Environment {
test_lang: String,
rust_log: String,
}
/// Handles the requests from `PolicycheckView`.
#[derive(AsAny, Default)]
pub struct PolicycheckState {
action: Option<PolicycheckAction>,
duration: Duration,
label_result: Entity,
lang: String,
//last_focused: Option<Entity>,
button_menu: Entity,
policy_data_count: u64,
policy_numbers: HashMap<u64, PolicyCode>,
popup_menu: Entity,
progress_bar: Entity,
progress_count: f64,
progress_popup: Entity
}
impl GlobalState for PolicycheckState {}
/// method definitions, that react on any given state change inside the view
impl PolicycheckState {
/// Sets a new action.
pub fn action(&mut self, action: PolicycheckAction) {
self.action = action.into();
}
/// Create a hashmap (key: policy number, value: policy type).
pub fn create_hashmap(&mut self, _ctx: &mut Context<'_>) -> Result<(), Box<dyn std::error::Error>> {
trace!(target: "advotracker", create_hashmap = "started");
let policy_list = PolicyList::new("policy list");
trace!(target: "advotracker", policy_list = ?policy_list);
// create vector to hold imported data
let res = t!("policy.string.label_policy_data", self.lang);
let mut policy_data = PolicyDataList::new(res);
trace!(target: "advotracker", policy_data = ?policy_data);
let mut policy_numbers : HashMap<u64, PolicyCode> = HashMap::new();
// Wip: use cli parameter stored in viperus ...
//let mut csv_import_path = v.get::<String>("import_file").unwrap();
let mut csv_import_path = String::from("POLLFNR_WOECHENTLICH.txt");
match import(&mut csv_import_path, &mut policy_data,
&mut policy_numbers, &mut self.policy_data_count,
&self.lang) {
Ok((count, duration)) => {
self.policy_data_count = count;
self.duration = duration;
trace!(target: "advotracker", csv_import_path = ?csv_import_path,
policy_data_count = ?&self.policy_data_count,
duration = ?&self.duration);
}
Err(err) => {
error!("error running CSV-Import: {}", err);
process::exit(1);
}
};
self.policy_numbers = policy_numbers;
trace!(target: "advotracker", create_hashmap = "finished");
Ok(())
}
/// Clear text in text box.
pub fn clear_entry(&mut self, _text_box: Entity, ctx: &mut Context<'_>) {
TextBox::text_set(&mut ctx.widget(), String::from(""));
}
/// Import policy numbers into hashmap
fn import_data(&mut self, ctx: &mut Context<'_>)
-> Result<(), Box<dyn std::error::Error>> {
// WIP: for now, only import once per session
if self.policy_data_count == 0 {
TextBlock::enabled_set(&mut ctx.child(ID_POLICY_CHECK_RESULT), true);
if self.policy_numbers.len() == 0 {
// initialize popup widget
//let sender = ctx.send_window_request();
self.set_popup_progress(ctx);
self.progress_count += 0.33;
self.update_progress_bar(ctx);
//sender.send(WindowRequest::Redraw).unwrap();
// for _ in 1..4 {
// self.progress_count += 0.33;
// self.update_progress_bar(ctx);
// sender.send(WindowRequest::Redraw).unwrap();
//}
// importing policy code elements from csv-file
match self.create_hashmap(ctx) {
Ok(()) => {
let res = t!("policy.hashmap.success", self.lang);
info!("hashmap has: {:?} entries", self.policy_data_count);
trace!(target: "advotracker",
hashmap_status = ?res,
hashmap_entries = ?self.policy_data_count);
self.progress_count = 1.;
self.update_progress_bar(ctx);
//sender.send(WindowRequest::Redraw).unwrap();
}
_ => {
let res = t!("policy.hashmap.failed", self.lang);
error!("{:?}", res);
trace!(target: "advotracker", hashmap_status = ?res);
}
}
}
} else {
trace!(target: "advotracker",
hashmap_status = "consume",
hashmap_entries = ?self.policy_data_count);
}
Ok(())
}
/// Open menu.
pub fn open_menu(&mut self, _entity: Entity, ctx: &mut Context<'_>) {
//let menu_string = ctx.get_widget(entity).get::<String16>("text");
//.child(policycheck_menu);
self.set_popup_menu(ctx);
}
/// Parse validity of the given policy number.
fn parse_entry(&mut self, policy_check_policy_number: Entity,
ctx: &mut Context<'_>) {
trace!(target: "advotracker", parse_entry = "started");
let policy_number_string = TextBox::text_clone(&mut ctx.get_widget(policy_check_policy_number));
let policy_number_length = policy_number_string.len();
if self.policy_data_count == 0 {
// Load data into hashmap
match self.import_data(ctx) {
Ok(()) => {
trace!(target: "advotracker", policycheck_state = "init", import_data = "success");
Stack::visibility_set(&mut ctx.child(ID_POLICY_DATA_STACK), Visibility::Visible);
let policy_data_count_string = format!("{:?}", &self.policy_data_count);
TextBlock::text_set(&mut ctx.child(ID_POLICY_DATA_COUNT), String::from(&policy_data_count_string));
},
Err(e) => trace!(target: "advotracker", policycheck_state = "init", import_data = ?e),
}
}
trace!(target: "advotracker", state = "parsing", policy_number = ?policy_number_string);
// Parse policy code: "AS-123456789"
// DION VERS POLLFNR
// 1 AS 1515735810
//Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_1), Visibility::Collapsed);
//Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_2), Visibility::Collapsed);
//TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_RESULT), Visibility::Collapsed);
//TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_RESULT), Visibility::Collapsed);
//TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_HINT), Visibility::Collapsed);
//TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_HINT), Visibility::Collapsed);
Button::background_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("transparent"));
//Button::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Collapsed);
if policy_number_length == 10 {
// cast policy_number_sting to <u64>
match policy_number_string.parse::<u64>() {
Ok(p) => {
TextBlock::text_set(&mut ctx.child(ID_POLICY_CHECK_RESULT), String::from(""));
// match hashmap's key
match self.policy_numbers.get(&p) {
Some(policy_code) => {
// matching key, get associated value
trace!(target: "advotracker", state = "success",
policy_number = ?p, policy_code = ?policy_code);
let string_result = format!("1-{:?}-{}",
policy_code, p);
TextBlock::text_set(&mut ctx.child(ID_POLICY_CHECK_RESULT), String::from(string_result));
TextBox::foreground_set(&mut ctx.child(ID_POLICY_CHECK_POLICY_NUMBER), String::from("#008000"));
Button::icon_brush_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#008000"));
Button::foreground_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#008000"));
Button::icon_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), material_icons_font::MD_CHECK);
Button::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Visible);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_1), Visibility::Visible);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_2), Visibility::Collapsed);
}
_ => {
// no matching key
let res = t!("policy.validation.failed", self.lang);
trace!(target: "advotracker", state = ?res, policy_number = ?p);
TextBox::foreground_set(&mut ctx.child(ID_POLICY_CHECK_POLICY_NUMBER), String::from("#FF0000"));
TextBlock::text_set(&mut ctx.child(ID_POLICY_CHECK_HINT), String::from("The given policy number is invalid"));
Button::icon_brush_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#FF0000"));
Button::foreground_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#FF0000"));
Button::icon_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), material_icons_font::MD_CLEAR);
Button::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Visible);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_1), Visibility::Collapsed);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_2), Visibility::Visible);
}
}
},
Err(e) => {
trace!(target: "advotracker", state = "error", error_type = "invalid type", error = ?e);
TextBox::foreground_set(&mut ctx.child(ID_POLICY_CHECK_POLICY_NUMBER), String::from("#FF0000"));
//TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_HINT), Visibility::Visible);
TextBlock::text_set(&mut ctx.child(ID_POLICY_CHECK_HINT), String::from("Only numbers are valid"));
//TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_HINT), Visibility::Visible);
Button::icon_brush_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#FF0000"));
Button::foreground_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#FF0000"));
Button::icon_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), material_icons_font::MD_CLEAR);
Button::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Visible);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_1), Visibility::Collapsed);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_2), Visibility::Visible);
}
}
}
if policy_number_length < 10 {
let res = t!("policy.validation.failed", self.lang);
trace!(target: "advotracker", state = ?res, reason = "number to short");
TextBox::foreground_set(&mut ctx.child(ID_POLICY_CHECK_POLICY_NUMBER), String::from("#FF0000"));
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_HINT), Visibility::Visible);
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_HINT), Visibility::Visible);
TextBlock::text_set(&mut ctx.child(ID_POLICY_CHECK_HINT), String::from("Policy number is to short"));
Button::icon_brush_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#FF0000"));
Button::foreground_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#FF0000"));
Button::icon_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), material_icons_font::MD_CLEAR);
Button::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Visible);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_1), Visibility::Collapsed);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_2), Visibility::Visible);
}
if policy_number_length > 10 {
let res = t!("policy.validation.failed", self.lang);
trace!(target: "advotracker", state = ?res, reason = "number to long");
TextBox::foreground_set(&mut ctx.child(ID_POLICY_CHECK_POLICY_NUMBER), String::from("#FF0000"));
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_HINT), Visibility::Visible);
TextBlock::text_set(&mut ctx.child(ID_POLICY_CHECK_HINT), String::from("Policy number is to long"));
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_HINT), Visibility::Visible);
Button::icon_brush_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#FF0000"));
Button::foreground_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), String::from("#FF0000"));
Button::icon_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), material_icons_font::MD_CLEAR);
Button::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Visible);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_1), Visibility::Collapsed);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_2), Visibility::Visible);
}
trace!(target: "advotracker", parse_entry = "finished");
}
/// Remove the menu popup box
fn remove_menu(&mut self, id: Entity, ctx: &mut Context<'_>) {
ctx.remove_child(self.popup_menu);
println!("Popup {:?} removed !", id);
}
/// Remove the popup box
fn remove_popup(&mut self, id: Entity, ctx: &mut Context<'_>) {
ctx.remove_child(self.progress_popup);
println!("Popup {:?} removed !", id);
}
// /// If TextBox 'policy_check_policy_number' is empty, disable button "clear"
// /// otherwise enabled it.
// fn set_policy_check_clear(&mut self, policy_check_policy_number: Entity, ctx: &mut Context<'_>) {
// button(&self, policy_check_policy_number: Entity, ctx: &mut Context<' >) {
// if ctx.get_widget(clear_button).get::<String>("policy_check_policy_number").is_empty() {
// ctx.get_widget(self.policy_check_clear_button).set("enabled", false);
// } else {
// ctx.get_widget(self.policy_check_clear_button).set("enabled", true);
// }
// ctx.get_widget(self.policy_check_policy_number).update_theme_by_state(true);
// }
/// Change status of given text box to edit mode.
fn set_entry(&mut self, text_box: Entity, ctx: &mut Context<'_>) {
if ctx.get_widget(text_box).get::<String16>("text").is_empty() {
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_RESULT), Visibility::Collapsed);
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Collapsed);
} else {
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_RESULT), Visibility::Visible);
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Visible);
}
}
/// Set a menu
fn set_popup_menu(&mut self, ctx: &mut Context<'_>) {
let stack = ctx
.entity_of_child(ID_POLICY_CHECK_BUTTON_MENU)
.expect("PolicycheckState: Can't find entity of resource 'ID_POLICY_CHECK_POPUP_MENU'.");
let current_entity = ctx.entity();
let build_context = &mut ctx.build_context();
// create a popup menu overlay
self.popup_menu = create_menu(current_entity, build_context);
// create a menu_popup widget as a child of entity "ID_POLICY_CHECK_BUTTON_MENU"
build_context.append_child(stack, self.popup_menu);
println!("Popup Menu created: {:?}", self.popup_menu);
}
/// Set a progress popup that updates the import status in a progress bar
fn set_popup_progress(&mut self, ctx: &mut Context<'_>) {
//println!("Set up Progress popup: {:?}", text_box);
let stack = ctx
.entity_of_child(ID_POLICY_CHECK_RESULT)
.expect("PolicycheckState: Can't find entity of resource 'ID_POLICY_CHECK_RESULT'.");
let current_entity = ctx.entity();
let build_context = &mut ctx.build_context();
self.progress_popup = create_popup_progress(current_entity, build_context);
// create a progress_popup widget as a child of entity "ID_POLICY_CHECK_POLICY_NUMBER"
build_context.append_child(stack, self.progress_popup);
self.progress_bar = ctx
.entity_of_child(ID_POLICY_CHECK_PROGRESS_BAR)
.expect("PolicycheckState.init: Can't find entity of resource 'ID_POLICY_CHECK_PROGRESS_BAR'.");
println!("PopupProgress created: {:?}", self.progress_popup);
}
/// Change visibility of the result label.
fn _set_visibility(&self, entity: Entity, ctx: &mut Context<'_>) {
if ctx.get_widget(entity).get::<String16>("text").is_empty() {
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_RESULT), Visibility::Collapsed);
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Collapsed);
} else {
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_RESULT), Visibility::Visible);
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_BUTTON_RESULT), Visibility::Visible);
}
//ctx.get_widget(self.label_result).update_theme_by_state(true);
}
/// Update count of elements in the policy data list.
fn _update_data_count(&self, ctx: &mut Context<'_>) {
let data_list_count = ctx.widget().get::<PolicyDataList>(PROP_POLICY_DATA_LIST).len();
ctx.widget().set(PROP_POLICY_DATA_COUNT, data_list_count);
}
fn update_progress_bar(&self, ctx: &mut Context<'_>) {
let res = t!("policy.string.progress_time", self.lang);
let string_duration = format!("{}: {:?}", res, self.duration);
TextBlock::text_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_TIME), String::from(string_duration));
let mut progress_bar = ctx.child(ID_POLICY_CHECK_PROGRESS_BAR);
progress_bar.set::<f64>("val", self.progress_count);
}
}
/// upported states for our view
impl State for PolicycheckState {
/// Initialize the widget state
fn init(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
let time_start= SystemTime::now();
trace!(target: "advotracker", policycheck_state = "init", status = "started");
// Entities
self.button_menu = ctx
.entity_of_child(ID_POLICY_CHECK_BUTTON_MENU)
.expect("PolicycheckState.init: Can't find resource entity 'ID_POLICY_CHECK_BUTTON_MENU'.");
self.label_result = ctx
.entity_of_child(ID_POLICY_CHECK_LABEL_RESULT)
.expect("PolicycheckState.init: Can't find resource entity 'ID_POLICY_CHECK_LABEL_RESULT'.");
//TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_RESULT), Visibility::Collapsed);
//TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_RESULT), Visibility::Collapsed);
//Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_0), Visibility::Collapsed);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_1), Visibility::Collapsed);
Stack::visibility_set(&mut ctx.child(ID_POLICY_CHECK_FORM_ROW_2), Visibility::Collapsed);
// Preset localization with given environment lang
self.lang = PolicycheckState::get_lang();
ctx.set_language(&self.lang);
// // Load the saved data from a file in 'ron' format into our data structure.
// // The cargo package identifier (default: 'nwx.advotracker') is used as the
// // app directory name. The directory location is OS dependant
// // (Windows: AppData, Unix: XDG_CONFIG_HOME, MacOS: $HOME/Library/Preferences).
// // The filename is taken from the propertey PROP_ADVOTRACKER (default: 'advotracker'.ron).
// if let Ok(policy_data) = registry
// .get::<Settings>("settings")
// .load::<PolicyDataList>(PROP_ADVOTRACKER)
// {
// ctx.widget().set(PROP_ADVOTRACKER, policy_data);
// }
let time_end = SystemTime::now();
let duration = time_end.duration_since(time_start);
trace!(target: "advotracker", policycheck_state = "init", status = "finished", duration = ?duration);
}
/// Update the widget state.
fn update(&mut self, _registry: &mut Registry, ctx: &mut Context<'_>) {
// // clear focus on focus moved
// if self.last_focused != ctx.window().get::<Global>("global").focused_widget {
// if let Some(last_focused) = self.last_focused {
// ctx.get_widget(last_focused).set("focused", false);
// // widget is unvisible, but takes space to be considered
// ctx.get_widget(last_focused)
// .set("visibility", Visibility::Collapsed);
// }
//}
if let Some(action) = self.action {
match action {
PolicycheckAction::AddProgress(increment) => {
let old_width = ProgressBar::val_clone(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR));
let new_width = old_width + increment;
// Set the ProgressBar's val property to the calculated percentage
// (whereas 0.0 means 0 %, and 1.0 means 100 %) to increment the progress
if new_width <= 1. {
ProgressBar::val_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR), new_width);
} else {
ProgressBar::val_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR), 1.);
}
}
PolicycheckAction::ChangeTheme() => {
println!("Changing active theme.");
}
PolicycheckAction::ClearEntry(policy_check_policy_number) => {
ctx.get_widget(policy_check_policy_number).set("enabled", false);
}
PolicycheckAction::InputTextChanged(entity) => {
println!("entry changed: {}", TextBox::text_clone(&ctx.get_widget(entity)));
}
PolicycheckAction::ImportData => {
match self.import_data(ctx) {
Ok(()) => {
trace!(target: "advotracker", import_data = "success");
}
_ => {
error!("Importing data failed!");
trace!(target: "advotracker", import_data = "failed");
}
}
}
PolicycheckAction::OpenMenu(entity) => {
self.open_menu(entity, ctx);
}
PolicycheckAction::ParseEntry(text_box) => {
self.parse_entry(text_box, ctx);
}
PolicycheckAction::RemoveFocus(policy_check_policy_number) => {
ctx.get_widget(policy_check_policy_number).set("enabled", false);
//ctx.EventAdapter(FocusEvent::RemoveFocus(policy_check_policy_number));
}
PolicycheckAction::RemoveMenu(entity) => {
self.remove_menu(entity, ctx);
}
PolicycheckAction::RemovePopup(entity) => {
self.remove_popup(entity, ctx);
}
PolicycheckAction::ResetProgress => {
ProgressBar::val_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR), 0.);
}
PolicycheckAction::SetEntry(policy_check_policy_number) => {
//self.last_focused = Some();
self.set_entry(policy_check_policy_number, ctx);
}
PolicycheckAction::SetMenu(_entity) => {
self.set_popup_menu(ctx);
}
PolicycheckAction::SetProgress(value) => {
if value >= 0. || value <= 1. {
ProgressBar::val_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR), value);
} else {
ProgressBar::val_set(&mut ctx.child(ID_POLICY_CHECK_PROGRESS_BAR), 0.);
} }
PolicycheckAction::SetProgressPopup(_entity) => {
self.set_popup_progress(ctx);
}
PolicycheckAction::SetVisibility(_entity) => {
TextBlock::visibility_set(&mut ctx.child(ID_POLICY_CHECK_LABEL_RESULT), Visibility::Collapsed);
}
PolicycheckAction::TextChanged(entity, _index) => {
self.set_entry(entity, ctx);
}
PolicycheckAction::ToggleTheme(_entity) => {
println!("TODO: toggle active theme");
//self.toggle_theme(entity, ctx);
}
}
}
// Reset action
self.action = None;
}
// /// Update the view after the layout is rendered.
// fn update_post_layout(&mut self, _: &mut Registry, _ctx: &mut Context<'_>) {
// }
}
/// Create a menu popup
fn create_menu(target: Entity, ctx: &mut BuildContext<'_>) -> Entity {
Popup::new()
.id(ID_POLICY_CHECK_POPUP_MENU)
.style("container_menu")
.target(target)
.open(true)
.width(280)
.height(140)
.on_mouse_down(move |ctx, _| {
println!("on_click -> remove_menu()");
ctx.get_mut::<PolicycheckState>(target)
.action(PolicycheckAction::RemoveMenu(target));
true
})
.child(
Grid::new()
.id(ID_POLICY_CHECK_MENU)
.columns(
Columns::create()
// Menu Button
.push("80")
// Seperator
.push("1")
// Keyboard Shortcut
.push("*")
.build(),
)
.rows(
Rows::create()
.push("auto")
.push("auto")
.push("auto")
.build(),
)
.child(
Button::new()
.id(ID_POLICY_CHECK_MENU_LABEL_ACCOUNT)
.style("button_menu")
.attach(Grid::row(0))
.attach(Grid::column(0))
.attach(Grid::column_span(2))
.icon(material_icons_font::MD_PERSON)
.text("Account")
.build(ctx),
)
.child(
Button::new()
.id(ID_POLICY_CHECK_MENU_LABEL_TOGGLE_THEME)
.style("button_menu")
.attach(Grid::row(1))
.attach(Grid::column(0))
.attach(Grid::column_span(2))
.icon(material_icons_font::MD_EDIT)
.text("Toggle theme")
.on_click(move |states, _| {
states.get_mut::<PolicycheckState>(target)
.action(PolicycheckAction::ToggleTheme(target));
true
})
.build(ctx),
)
.child(
Button::new()
.id(ID_POLICY_CHECK_MENU_LABEL_QUIT)
.style("button_menu")
.attach(Grid::row(2))
.attach(Grid::column(0))
.attach(Grid::column_span(2))
.icon(material_icons_font::MD_SETTINGS_POWER)
.text("Quit")
.on_mouse_down(move |_states, _| {
process::exit(0);
})
.build(ctx),
)
.child(
TextBlock::new()
.id(ID_POLICY_CHECK_MENU_SHORTCUT_QUIT)
.style("button_menu")
.attach(Grid::row(2))
.attach(Grid::column(2))
.margin((0, 0, 16, 0))
.h_align("end")
.v_align("center")
.text("CTRL+Q")
.build(ctx),
)
.build(ctx),
)
.build(ctx)
}
/// Create a progress popup with update status of an onging data import
fn create_popup_progress(target: Entity, ctx: &mut BuildContext<'_>) -> Entity {
Popup::new()
.id(ID_POLICY_CHECK_POPUP_PROGRESS)
.target(target)
.open(true)
//.style("popup_progress")
.width(280)
.height(100)
.on_mouse_down(move |ctx, _| {
println!("on_click -> remove_popup_progress()");
ctx.get_mut::<PolicycheckState>(target)
.action(PolicycheckAction::RemovePopup(target));
true
})
.child(
Container::new()
.style("container_progress")
.child(
Stack::new()
.style("stack_progress")
.child(
TextBlock::new()
.id(ID_POLICY_CHECK_PROGRESS_TEXT)
//.style("textblock_progress")
.font_size(12)
.text("Importing data")
.build(ctx)
)
.child(
ProgressBar::new()
.id(ID_POLICY_CHECK_PROGRESS_BAR)
.val(0)
//.width(250)
.build(ctx)
)
.child(
TextBlock::new()
.id(ID_POLICY_CHECK_PROGRESS_TIME)
//.style("textblock_progress")
.h_align("end")
.font_size(12)
.text("Processing time")
.build(ctx)
)
.build(ctx)
)
.build(ctx)
)
.build(ctx)
}

View File

@@ -0,0 +1,397 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use orbtk::prelude::*;
use crate::{
data::{
constants::*,
structures::PolicyCheck,
},
widgets::policycheck::policycheck_state::*,
};
// Macro that initializes the widget structures/variables for our view
widget!(
/// Dialog to enter a policy identifier/number.
/// This identifier is checked agains a map of valid policy codes.
PolicycheckView<PolicycheckState> {
lang: String,
policy_check: PolicyCheck,
policy_check_title: String,
policy_data_count: u32
}
);
/// The template implementation of the policy check view
/// All GUI elements are styled using the "style" attribute referencing to a ron based css
impl Template for PolicycheckView {
//fn template(self, policycheck_view: Entity, ctx: &mut BuildContext<'_>) -> Self {
fn template(self, id: Entity, ctx: &mut BuildContext<'_>) -> Self {
let policy_check_bottom_bar = Container::new()
//.style(STYLE_BOTTOM_BAR)
.attach(Grid::row(4))
.attach(Grid::column(1))
.attach(Grid::column_span(2))
.v_align("end")
.child(
Container::new()
.child( Container::new()
.margin((0, 16, 16, 0))
.v_align("center")
.child(
ImageWidget::new()
.image("assets/advotracker/hiedemann_logo.png")
.v_align("center")
.build(ctx),
)
.build(ctx),
)
.build(ctx),
)
.child(
Container::new()
.attach(Grid::column(1))
.h_align("end")
.v_align("end")
.child(
TextBlock::new()
.margin((0, 9, 48, 0))
.text("©Networkx GmbH")
.build(ctx)
)
.build(ctx),
)
.build(ctx);
let policy_check_label_menu = TextBlock::new()
.id(ID_POLICY_CHECK_LABEL_MENU)
.style("menu")
.text("Help Menu")
.v_align("center")
.h_align("center")
.build(ctx);
let policy_check_button_menu = Button::new()
.id(ID_POLICY_CHECK_BUTTON_MENU)
.style("button_single_content")
.icon(material_icons_font::MD_MENU)
.attach(Grid::column(2))
//.min_size(16, 16)
.h_align("end")
.on_click(move |ctx, _| {
//ctx.get_mut::<PolicycheckState>(policycheck_state)
ctx.get_mut::<PolicycheckState>(id)
.action(PolicycheckAction::OpenMenu(policy_check_label_menu));
true
})
.build(ctx);
let policy_check_button_result = Button::new()
.id(ID_POLICY_CHECK_BUTTON_RESULT)
.style("button_single_content")
.h_align("start")
.v_align("center")
.visibility(Visibility::Collapsed)
.enabled(false)
.build(ctx);
let policy_check_header_bar = Container::new()
.style(STYLE_HEADER_BAR)
.attach(Grid::row(0))
.attach(Grid::column(1))
.attach(Grid::column_span(2))
.child(
Grid::new()
.child(
TextBlock::new()
.style("header")
.id(ID_POLICY_CHECK_HEADER)
.v_align("center")
.h_align("left")
.text("Validation policy number")
.build(ctx),
)
.build(ctx),
)
.child(policy_check_button_menu)
.build(ctx);
let policy_check_form_row_0 = Stack::new()
// 1st row
.id(ID_POLICY_CHECK_FORM_ROW_0)
.attach(Grid::row(0))
.attach(Grid::column(0))
.attach(Grid::column_span(5))
.orientation("horizontal")
.v_align("center")
//.h_align("center")
.child(
TextBlock::new()
.id(ID_POLICY_CHECK_LABEL_POLICY_NUMBER)
.style("body")
.attach(Grid::row(0))
.attach(Grid::column(0))
.margin((0, 0, 16, 0))
.h_align("end")
.v_align("center")
.width(300)
.min_width(300)
.text("Policy number")
.build(ctx),
)
.child(
TextBox::new()
.attach(Grid::row(0))
.attach(Grid::column(2))
.id(ID_POLICY_CHECK_POLICY_NUMBER)
.h_align("start")
.lose_focus_on_activation(false)
//WIP: localization for water_mark
.water_mark("10-stellig")
.on_activate(move |ctx, entity| {
// Entity is entered/activated via Mouse/Keyboard
//ctx.get_mut::<PolicycheckState>(policy_check_view)
println!("ParseEntry: {:?}", entity);
ctx.get_mut::<PolicycheckState>(id)
.action(PolicycheckAction::ParseEntry(entity));
})
//.on_changed(|_, entity, _, _| println!("Selection changed: {:?}", entity))
// .on_changed(move |ctx, entity, _| {
// ctx.get_mut::<PolicycheckState>(id)
// .action(Action::SetProgressBox(entity));
// ctx.get_mut::<PolicycheckState>(id)
// .action(Action::AddProgress(0.5));
// ctx.get_mut::<PolicycheckState>(id)
// .action(Action::InputTextChanged(entity));
// ctx.get_mut::<PolicycheckState>(id)
// .action(Action::SetVisibility(entity));
// ctx.get_widget(policy_check_label_policy_number).set("visible");
// ctx.get_mut::<PolicycheckState>(id)
// .action(Action::SetVisility(entity));
// })
// .on_mouse_down | .on_mouse_move | .on_mouse_up (move |ctx, entity| {
// state(id, states).action(Action::AddItem);
// })
//.on_key_down(move |ctx, _| {
// ctx.get_mut::<PolicycheckState>(id)
// .action(Action::ImportData);
// true
//})
.build(ctx),
)
.child(policy_check_button_result)
.build(ctx);
let policy_check_form_row_1 = Stack::new()
// 2nd row
.id(ID_POLICY_CHECK_FORM_ROW_1)
.attach(Grid::row(1))
.attach(Grid::column(0))
.attach(Grid::column_span(5))
.orientation("horizontal")
.v_align("center")
//.h_align("center")
.child(
TextBlock::new()
.id(ID_POLICY_CHECK_LABEL_RESULT)
.style("body")
.attach(Grid::row(1))
.attach(Grid::column(0))
.margin((0, 0, 16, 0))
.h_align("end")
.v_align("center")
.width(300)
.min_width(300)
.text("Policy code")
.build(ctx),
)
.child(
TextBlock::new()
.id(ID_POLICY_CHECK_RESULT)
.style("body")
.attach(Grid::row(1))
.attach(Grid::column(2))
.h_align("start")
.v_align("center")
.text("empty")
.build(ctx)
)
.build(ctx);
let policy_check_form_row_2 = Stack::new()
// 3nd row
.id(ID_POLICY_CHECK_FORM_ROW_2)
.attach(Grid::row(2))
.attach(Grid::column(0))
.attach(Grid::column_span(5))
.orientation("horizontal")
.v_align("center")
.h_align("center")
.child(
TextBlock::new()
.id(ID_POLICY_CHECK_LABEL_HINT)
//.style("hint")
.attach(Grid::row(2))
.attach(Grid::column(0))
.margin((0, 0, 16, 0))
.h_align("end")
.v_align("center")
.width(300)
.min_width(300)
.font_size(11.)
.text("Error:")
.build(ctx),
)
.child(
TextBlock::new()
.id(ID_POLICY_CHECK_HINT)
//.style("hint")
.attach(Grid::row(2))
.attach(Grid::column(2))
.h_align("start")
.v_align("center")
.font_size(11)
.build(ctx)
)
.build(ctx);
let policy_check_form = Container::new()
.id(ID_POLICY_CHECK_FORM)
.min_width(420)
.attach(Grid::row(2))
.attach(Grid::column(1))
.style("container_form")
.child(
Grid::new()
.id(ID_POLICY_CHECK_FORM)
.width(450)
.min_width(400)
.columns(
Columns::create()
// Label
.push("300")
// Seperator
.push("16")
// Values
.push("auto")
// Seperator
.push("16")
// Result-Button
.push("32")
//.build(),
)
.rows(
Rows::create()
.push("auto")
.push("auto")
.push("*")
//.build(),
)
.child(policy_check_form_row_0)
.child(policy_check_form_row_1)
.child(policy_check_form_row_2)
.build(ctx),
)
.build(ctx);
// WIP: this widget type should handle our target natively
// but it isn't able to get textinput ... yet
let _policy_check_numeric_box = NumericBox::new()
.id(ID_POLICY_CHECK_POLICY_NUMBER)
.h_align("start")
.width(100)
//.min(<usize>1000000000)
//.max(<usize>9999999999)
.val(0)
.min_width(95)
.margin((0, 0, 0, 16))
// WIP code @kivimango
// .on_activate(move |ctx, entity| {
// ctx.get_mut::<PolicycheckState>(id)
// .action(Action::ParseEntry(entity));
// })
// .on_changed(move |ctx, entity| {
// ctx.get_mut::<PolicycheckState>(id)
// .action(Action::InputTextChanged(entity));
//})
.build(ctx);
let policy_data_stack = Stack::new()
.id(ID_POLICY_DATA_STACK)
.attach(Grid::row(3))
.attach(Grid::column(1))
.h_align("end")
.v_align("top")
.orientation("horizontal")
.visibility(Visibility::Collapsed)
//.spacing("4")
.child(
TextBlock::new()
.id(ID_POLICY_DATA_LABEL)
.margin((0, 4, 0, 0))
.enabled(true)
.text("Checklist elements: ")
.build(ctx)
)
.child(
TextBlock::new()
.id(ID_POLICY_DATA_COUNT)
.margin((0, 4, 0, 0))
.enabled(true)
.text("0")
.build(ctx)
)
.build(ctx);
// Starter page: check policy numbers
self.name("PolicycheckView")
// initialize struct (derived default macro)
.policy_check(PolicyCheck::default())
.child(
Grid::new()
.id(ID_POLICY_CHECK_WIDGET)
.columns(
Columns::create()
.push(50)
.push("*")
.push(50)
//.build(),
)
.rows(
Rows::create()
.push("auto")
.push(28)
.push("*")
.push("auto")
.push("auto")
//.build(),
)
// row 0: Policy Check Header
.child(policy_check_header_bar)
// row 1: Seperator
// row 2: Policy Check Form
.child(policy_check_form)
// row 3: Sum HashMap elements
.child(policy_data_stack)
// row 4: Policy Check Bottom
.child(policy_check_bottom_bar)
.build(ctx),
)
}
}
// helper to request PolicycheckState
//fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut PolicycheckState {
// states.get_mut(id)
//}

View File

@@ -0,0 +1,12 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
/// The policy check state
pub mod policycheck_state;
/// The policy check view
pub mod policycheck_view;

View File

@@ -0,0 +1,290 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use orbtk::prelude::*;
use crate::{
callbacks::global_state::GlobalState,
data::constants::*,
data::{PolicyCode, PolicyData, PolicyList, Status},
};
use chrono::{Utc, NaiveDateTime};
/// Actions that can execute on the task view.
#[derive(Debug, Clone, Copy)]
pub enum Action {
InputTextChanged(Entity),
NavigateBack(),
NewEntry(Entity),
RemoveFocus(Entity),
RemoveEntry(usize),
SetDateInserted(Entity),
SetDion(Entity),
SetPolicyCode(Entity),
SetPolicyNumber(Entity),
StatusChanged(Entity, usize),
TextChanged(Entity, usize),
}
/// Handles the requests of the `PolicyDataListView`.
#[derive(Debug, Default, AsAny)]
pub struct PolicyDataState {
action:Option<Action>,
back_entity: Entity,
last_focused: Option<Entity>,
policy_data_add_button: Entity,
pub policy_data_date_inserted: Entity,
pub policy_data_dion: Entity,
pub policy_data_policy_code: Entity,
pub policy_data_policy_number: Entity,
open: bool,
}
/// implement of the BaseState trait for our PolicyDataState type
impl GlobalState for PolicyDataState {}
/// implement the PolicyDateState type itself
impl PolicyDataState {
/// Sets a new action.
pub fn action(&mut self, action: Action) {
self.action = action.into();
}
/// update number of available policy data entries.
fn adjust_count(&self, ctx: &mut Context) {
if let Some(index) = ctx.widget().clone::<Option<usize>>("list_index") {
if let Some(policy_list) = ctx
.widget()
.clone::<PolicyList>("policy_list")
.get(index)
{
ctx.widget().set("policy_data_count", policy_list.len());
}
}
}
/// If policy data element 'name' is empty, disable the add button
/// otherwise otherwise enabled it.
fn adjust_add_button_enabled(&self, text_box: Entity, ctx: &mut Context) {
if ctx.get_widget(text_box).get::<String16>("name").is_empty() {
ctx.get_widget(self.policy_data_add_button).set("enabled", false);
} else {
ctx.get_widget(self.policy_data_add_button).set("enabled", true);
}
ctx.get_widget(self.policy_data_add_button).update_theme_by_state(true);
}
/// Change status of given text box to edit mode.
fn edit_entry(&self, text_box: Entity, ctx: &mut Context) {
if *ctx.get_widget(text_box).get::<bool>("focused") {
ctx.get_widget(text_box).set("enabled", false);
ctx.push_event_by_window(FocusEvent::RemoveFocus(text_box));
return;
}
if let Some(old_focused_element) = ctx.window().get::<Global>("global").focused_widget {
ctx.push_event_by_window(FocusEvent::RemoveFocus(old_focused_element));
}
ctx.get_widget(text_box).set("enabled", true);
// select all
ctx.get_widget(text_box)
.get_mut::<TextSelection>("text_selection")
.start_index = 0;
ctx.get_widget(text_box)
.get_mut::<TextSelection>("text_selection")
.length = ctx.get_widget(text_box).get::<String16>("name").len();
ctx.push_event_by_window(FocusEvent::RequestFocus(text_box));
}
/// navigate to previous policy data entry.
fn navigate_back(&mut self, ctx: &mut Context) {
ctx.get_widget(self.text_box)
.set("name", String16::from(""));
self.open = false;
ctx.widget().set::<Option<usize>>("list_index", None);
ctx.widget().set("policy_data_count", 0 as usize);
self.navigate(self.back_entity, ctx);
}
/// create a new policy data member
fn new_entry(&self, policy_number: u32, registry: &mut Registry, ctx: &mut Context) {
let index = ctx.widget().clone::<Option<usize>>("list_index");
if let Some(index) = index {
if let Some(policy_data) = ctx
.widget()
.get_mut::<PolicyList>("policy_list")
.get_mut(index)
{
policy_data.push(PolicyData {
date_inserted: Some(Utc::now().naive_utc()),
dion: 1,
policy_code: PolicyCode::AS,
policy_number,
status: Status::Active,
});
}
self.adjust_count(ctx);
}
self.save(registry, ctx);
}
pub fn open(&mut self, ctx: &mut Context) {
if let Some(index) = ctx.widget().clone::<Option<usize>>("list_index") {
let mut name: String16 = "".into();
let mut policy_data_count = 0;
if let Some(policy_list) = ctx.widget().get::<PolicyList>("policy_list").get(index) {
name = String16::from(policy_list.name.as_str());
policy_data_count = policy_list.len();
}
ctx.widget().set("name", name);
ctx.widget().set("policy_data_count", policy_data_count);
self.open = true;
}
}
fn remove_entry(&self, index: usize, registry: &mut Registry, ctx: &mut Context) {
if let Some(idx) = ctx.widget().clone::<Option<usize>>("list_index") {
if let Some(policy_list) = ctx
.widget()
.get_mut::<PolicyList>("policy_list")
.get_mut(idx)
{
policy_list.remove(index);
}
}
self.adjust_count(ctx);
self.save(registry, ctx);
}
/// Toggle the selection state of the given policy number element
fn toggle_status(
&self,
entry: Entity,
index: usize,
registry: &mut Registry,
ctx: &mut Context,
) {
let invalid: bool = *ctx.get_widget(entry).get("status");
if let Some(idx) = ctx.widget().clone::<Option<usize>>("list_index") {
if let Some(policy_data) = ctx
.widget()
.get_mut::<PolicyList>("policy_list")
.get_mut(idx)
{
if let Some(policy_data) = policy_data.get_mut(index) {
policy_data.status = Status::Active;
}
}
}
self.save(registry, ctx);
}
fn update_entry(
&self,
policy_data_date_inserted: Entity,
policy_data_dion: Entity,
policy_data_policy_code: Entity,
policy_data_policy_number: Entity,
index: usize,
registry: &mut Registry,
ctx: &mut Context,
) {
let date_inserted: Option<NaiveDateTime> = ctx.get_widget(policy_data_date_inserted).clone("date_inserted");
let dion: u8 = ctx.get_widget(policy_data_dion).clone("dion");
let policy_code: PolicyCode = ctx.get_widget(policy_data_policy_code).clone("policy_code");
let policy_number: u32 = ctx.get_widget(policy_data_policy_number).clone("policy_number");
// get the index from the active widgets 'list_index' member
if let Some(idx) = ctx.widget().clone::<Option<usize>>("list_index") {
// get view record from the active widgets 'policy_list' member
if let Some(policy_list) = ctx
.widget()
.get_mut::<PolicyList>("policy_list")
.get_mut(idx)
{
// update the record data using the view values of the active policy_list member
if let Some(policy_data) = policy_list.get_mut(index) {
policy_data.date_inserted = date_inserted;
policy_data.dion = dion;
policy_data.policy_code = policy_code;
policy_data.policy_number = policy_number;
}
}
}
self.save(registry, ctx);
}
}
impl State for PolicyDataState {
fn init(&mut self, _: &mut Registry, ctx: &mut Context) {
self.back_entity = (*ctx.widget().get::<u32>("back_entity")).into();
self.policy_data_add_button = ctx
.entity_of_child(ID_POLICY_DATA_ADD_BUTTON)
.expect("PolicyState.init: Can't find child 'Add button'.");
self.policy_data_policy_number = ctx
.entity_of_child(ID_POLICY_DATA_POLICY_NUMBER)
.expect("PolicyState.init: Can't find child 'Policy Number'.");
}
fn update(&mut self, registry: &mut Registry, ctx: &mut Context) {
if !self.open {
self.open(ctx);
}
if let Some(action) = self.action {
match action {
Action::InputTextChanged(text_box) => {
self.adjust_add_button_enabled(text_box, ctx);
}
Action::NewEntry(entity) => {
if let Some(text) = self.fetch_text(ctx, entity) {
self.new_entry(text, registry, ctx);
}
}
Action::RemoveEntry(index) => {
self.remove_entry(index, registry, ctx);
}
Action::SelectionChanged(entity, index) => {
self.toggle_selection(entity, index, registry, ctx);
}
Action::TextChanged(entity, index) => {
self.update_entry(entity, index, registry, ctx);
}
Action::SetDateInserted(policy_data_date_inserted) => {
self.last_focused = Some(policy_data_date_inserted);
self.edit_entry(policy_data_date_inserted, ctx);
}
Action::SetDion(policy_data_dion) => {
self.last_focused = Some(policy_data_dion);
self.edit_entry(policy_data_policy_dion);
}
Action::SetPolicyCode(policy_data_policy_code) => {
self.last_focused = Some(policy_data_policy_code);
self.edit_entry(policy_data_policy_code, ctx);
}
Action::SetPolicyNumber(policy_data_policy_number) => {
self.last_focused = Some(policy_data_policy_number);
self.edit_entry(policy_data_policy_number, ctx);
}
Action::RemoveFocus(text_box) => {
ctx.get_widget(text_box).set("enabled", false);
ctx.push_event_by_window(FocusEvent::RemoveFocus(text_box));
}
Action::NavigateBack() => {
self.navigate_back(ctx);
}
}
}
self.action = None;
}
}

View File

@@ -0,0 +1,394 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use orbtk::prelude::*;
use crate::{
constants::*,
data::{PolicyList, Status},
policydata_state::{Action, PolicyDataState},
};
use chrono::NaiveDateTime;
type ListIndex = Option<usize>;
// Dialog to manage policy identifiers.
// The elements may be toggled to be deactivated. The optional value 'data_valid_until'
// will render this value unvalid after the given date.
widget!(
PolicyDataView<PolicyDataState> {
back_entity: u32,
policy_list_count: usize,
policylist_view: u32,
list_index: ListIndex,
date_valid_until: NaiveDateTime,
policy_list: PolicyList
});
impl Template for PolicyDataView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
// listing the policy elements
let items_widget = ItemsWidget::new()
.id(ID_POLICY_DATA_ITEMS_WIDGET)
.v_align("start")
.items_builder(move |ctx, index| {
let mut date_inserted = None;
let mut dion = 1;
let mut policy_code = "".to_string();
let mut policy_number = None;
let mut status = false; // Status::<Active, Inactive>;
if let Some(list_index) = ctx.get_widget(id).clone::<ListIndex>("list_index") {
if let Some(policy_data) = ctx
.get_widget(id)
.get::<PolicyList>(PROP_POLICY_LIST)
.get(list_index)
{
if let Some(policy_data) = policy_data.get(index) {
date_inserted = policy_data.date_inserted.clone();
dion = policy_data.dion.clone();
policy_code = policy_data.policy_code.clone();
policy_number = policy_data.policy_number.clone();
status = policy_data.status;
}
}
}
let policy_data_status = CheckBox::new()
.attach(Grid::column(1))
.v_align("center")
// status: true -> State::Active false -> State::Inactive
.enabled(status)
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::StatusChanged(entity, index));
})
.build(ctx);
let policy_data_dion = TextBox::new()
.id(ID_POLICY_DATA_DION)
.attach(Grid::row(3))
.v_align("center")
.margin((4.0, 0.0, 0.0, 0.0))
.text(dion)
.lost_focus_on_activation(false)
.on_activate(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::CreateEntry(entity));
})
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::InputTextChanged(entity));
})
.build(ctx);
let policy_data_dion = TextBox::new()
.id(ID_POLICY_DATA_DION)
.attach(Grid::row(5))
.v_align("center")
.margin((4.0, 0.0, 0.0, 0.0))
.text(policy_code)
.lost_focus_on_activation(false)
.on_activate(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::TextChanged(entity));
})
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::RemoveFocus(entity));
})
.build(ctx);
let policy_data_policy_code = TextBox::new()
.id(ID_POLICY_DATA_POLICY_CODE)
.attach(Grid::row(7))
.v_align("center")
.margin((4.0, 0.0, 0.0, 0.0))
.text(policy_code)
.lost_focus_on_activation(false)
.on_activate(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::TextChanged(entity));
})
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::RemoveFocus(entity));
})
.build(ctx);
let policy_data_policy_number = TextBox::new()
.id(ID_POLICY_DATA_POLICY_NUMBER)
.attach(Grid::column(9))
.text(policy_number)
.enabled(false)
.v_align("center")
.water_mark("Insert the new policy number ...")
.class("inplace")
.on_activate(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::TextChanged(entity));
})
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::RemoveFocus(entity));
})
.build(ctx);
let policy_data_date_inserted = TextBlock::new()
.id(ID_POLICY_DATA_DATE_INSERTED)
.attach(Grid::column(9))
.text(date_inserted)
.v_align("start")
.on_activate(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::TextChanged(entity));
})
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::RemoveFocus(entity));
})
.build(ctx);
Grid::new()
.height(48.0)
.columns(
Columns::new()
.add(10.0)
.add(24.0)
.add(8.0)
.add(24.0)
.add(8.0)
.add(24.0)
.add(8.0)
.add("auto")
.add(8.0)
.add("auto")
.add(8.0)
.add(32.0)
.add(4.0)
.add(32.0)
.add(8.0)
.build(),
)
.child(policy_data_status)
.child(policy_data_dion)
.child(policy_data_policy_code)
.child(policy_data_policy_number)
.child(policy_data_date_inserted)
.child(
// edit active policy data element
Button::new()
.class(CLASS_ICON_ONLY)
.attach(Grid::column(11))
.min_size(32.0, 32.0)
.v_align("center")
.icon(material_icons_font_ttf::MD_EDIT)
//.icon("")
.on_mouse_down(|_, _| true)
.on_click(move |ctx, _| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::EditEntry(text_box));
true
})
.build(ctx),
)
.child(
// delete active policy data element
Button::new()
.class("icon_only")
.attach(Grid::column(13))
.min_size(32.0, 32.0)
.v_align("center")
.icon(material_icons_font_ttf::MD_DELETE)
//.icon("")
.on_mouse_down(|_, _| true)
.on_click(move |ctx, _| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::RemoveEntry(index));
true
})
.build(ctx),
)
.build(ctx)
})
.policy_data_count(PROP_POLICY_DATA_COUNT, id)
.build(ctx);
let scroll_viewer = ScrollViewer::new()
.scroll_viewer_mode(("disabled", "auto"))
.child(items_widget)
.build(ctx);
let policy_data_list_name = TextBox::new()
.id(ID_POLICY_DATA_LIST_NAME)
.attach(Grid::row(4))
.v_align("center")
.margin((4.0, 0.0, 0.0, 0.0))
.lost_focus_on_activation(false)
.on_activate(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::CreateEntry(entity));
})
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::InputTextChanged(entity));
})
.build(ctx);
// Child page: list available policy data elements
self.name("PolicyDataListView").child(
Grid::new()
.rows(
Rows::new()
.add(52.0)
.add(1.0)
.add("*")
.add(1.0)
.add("auto")
.build(),
)
.columns(
Columns::new()
.add("*")
.add(4.0)
.add(36.0)
.build(),
)
// Top Bar
.child(
Container::new()
.class(CLASS_TOP_BAR)
.attach(Grid::row(0))
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.child(
Grid::new()
.columns(
Columns::new()
.add(32.0)
.add(4.0)
.add("*")
.add(4.0)
.add(32.0)
.build(),
)
.child(
Button::new()
.height(32.0)
.icon(material_icons_font_ttf::MD_ARROW_LEFT)
//.icon("")
.class(CLASS_ICON_ONLY)
.v_align("center")
.on_click(move |ctx, _| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::NavigateBack());
true
})
.build(ctx),
)
.child(
TextBlock::new()
.class(CLASS_HEADER)
.attach(Grid::column(2))
.v_align("center")
.h_align("center")
.text(("name", id))
.build(ctx),
)
.child(
Stack::new()
.class(CLASS_HEADER)
.attach(Grid::column(3))
.h_align("start")
.orientation("horizontal")
.child(
TextBox::new()
.class(CLASS_HEADER)
//.v_align("center")
.h_align("start")
.text(("Anzahl:", id))
.build(ctx),
)
.child(
TextBlock::new()
.class(CLASS_HEADER)
.id(PROP_POLICY_DATA_COUNT)
.attach(Grid::column(3))
//.v_align("center")
.h_align("end")
.text("policy_data_count")
.build(ctx),
)
.build,
)
.build(ctx),
)
.build(ctx),
)
.child(
Container::new()
.class("separator")
.attach(Grid::row(1))
.attach(Grid::column_span(3))
.build(ctx),
)
// Content
.child(
Container::new()
.attach(Grid::row(2))
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.child(scroll_viewer)
.child(
ScrollIndicator::new()
.padding((0.0, 4.0, 0.0, 0.0))
.content_id(items_widget.0)
.scroll_offset(scroll_viewer)
.build(ctx),
)
.build(ctx),
)
.child(
Container::new()
.class("separator")
.attach(Grid::row(3))
.attach(Grid::column_span(3))
.build(ctx),
)
// Bottom bar
.child(
Container::new()
.class(CLASS_BOTTOM_BAR)
.attach(Grid::row(4))
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.build(ctx),
)
.child(policy_data_list_name)
.child(
Button::new()
.id(ID_POLICY_DATA_ADD_BUTTON)
.class(CLASS_ICON_ONLY)
.attach(Grid::row(4))
.attach(Grid::column(2))
.margin((0.0, 0.0, 4.0, 0.0))
.enabled(false)
.min_size(32.0, 32.0)
.v_align("center")
.icon(material_icons_font_ttf::MD_ADD_CIRCLE)
.on_click(move |ctx, _| {
ctx.get_mut::<PolicyDataState>(id)
.action(Action::CreateEntry(policy_data_list_name));
true
})
.build(ctx),
)
.build(ctx),
)
}
}

View File

@@ -0,0 +1,12 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
/// The policy check state
pub mod policylist_state;
/// The policy check view
pub mod policylist_view;

View File

@@ -0,0 +1,203 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use orbtk::prelude::*;
use crate::{
base_state::BaseState,
constants::*,
data::{PolicyDataList, PolicyList},
};
/// Actions that can execute on the task view.
#[derive(Debug, Clone, Copy)]
pub enum Action {
InputTextChanged(Entity),
NewEntry(Entity),
RemoveFocus(Entity),
RemoveEntry(usize),
OpenPolicyList(usize),
SetEntry(Entity),
TextChanged(Entity, usize),
}
/// Handles the requests of the `PolicyListView`.
#[derive(Default, AsAny)]
pub struct PolicyListState {
action:Option<Action>,
add_button: Entity,
items_widget: Entity,
policydata_view: Entity,
last_focused: Option<Entity>,
text_box: Entity,
}
impl BaseState for PolicyListState {}
impl PolicyListState {
/// Sets a new action.
pub fn action(&mut self, action: Action) {
self.action = action.into();
}
/// If policy data element 'name' is empty, disable the add button
/// otherwise otherwise enabled it.
fn adjust_add_button_enabled(&self, text_box: Entity, ctx: &mut Context) {
/* Old syntax
if ctx.get_widget(text_box).get::<String>("name").is_empty() {
ctx.get_widget(self.add_button).set("enabled", false);
} else {
ctx.get_widget(self.add_button).set("enabled", true);
}
ctx.get_widget(self.add_button).update_theme_by_state(true);
*/
if TextBox::get(ctx.get_widget(text_box)).text().is_empty() {
Button::get(ctx.child("add_button")).set_enabled(false);
} else {
Button::get(ctx.child("add_button")).set_enabled(true);
}
// new syntax not implemented yet
ctx.get_widget(self.add_button).update_theme_by_state(true);
}
/// update number of available policy list entries.
fn adjust_count(&self, ctx: &mut Context) {
let policy_list_count = ctx.widget().get::<PolicyList>(PROP_POLICY_LIST).len();
ctx.widget().set(PROP_POLICY_LIST_COUNT, policy_list_count);
}
// Creates a new policy data list.
fn new_entry(&self, name: String, registry: &mut Registry, ctx: &mut Context) {
ctx.widget()
.get_mut::<PolicyList>(PROP_POLICY_LIST)
.push(PolicyList::new(name));
self.adjust_count(ctx);
self.save(registry, ctx);
}
// opens a given policy list name.
fn open_policy_list(&self, index: usize, ctx: &mut Context) {
ctx.get_widget(self.text_box)
.set("text", String16::from(""));
ctx.get_widget(self.policydata_view)
.set("list_index", Some(index));
self.navigate(self.policydata_view, ctx);
}
// removes a policy list.
fn remove_entry(&self, index: usize, registry: &mut Registry, ctx: &mut Context) {
ctx.widget()
.get_mut::<PolicyList>(PROP_POLICY_LIST)
.remove(index);
self.adjust_count(ctx);
self.save(registry, ctx);
}
/// Change status of given text box to edit mode.
fn set_entry(&self, text_box: Entity, ctx: &mut Context) {
if *ctx.get_widget(text_box).get::<bool>("focused") {
ctx.get_widget(text_box).set("enabled", false);
ctx.push_event_by_window(FocusEvent::RemoveFocus(text_box));
return;
}
if let Some(old_focused_element) = ctx.window().get::<Global>("global").focused_widget {
ctx.push_event_by_window(FocusEvent::RemoveFocus(old_focused_element));
}
ctx.get_widget(text_box).set("enabled", true);
// select all
ctx.get_widget(text_box)
.get_mut::<TextSelection>("text_selection")
.start_index = 0;
ctx.get_widget(text_box)
.get_mut::<TextSelection>("text_selection")
.length = ctx.get_widget(text_box).get::<String16>("name").len();
ctx.push_event_by_window(FocusEvent::RequestFocus(text_box));
}
}
impl State for PolicyListState {
fn init(&mut self, registry: &mut Registry, ctx: &mut Context) {
self.text_box = ctx
.entity_of_child(ID_POLICY_LIST_TEXT_BOX)
.expect("PolicyListState.init: Child 'Text box' not found.");
self.add_button = ctx
.entity_of_child(ID_POLICY_LIST_ADD_BUTTON)
.expect("PolicyListState.init: Child 'Add button' not found.");
self.items_widget = ctx
.entity_of_child(ID_POLICY_LIST_ITEMS_WIDGET)
.expect("PolicyListState.init: Child 'Items widget' not found.");
self.policydata_view = (*ctx.widget()
.get::<u32>("policy_list_view")).into();
if let Ok(policy_list_name) = registry
.get::<Settings>("settings")
.load::<PolicyList>(PROP_POLICY_LIST)
{
ctx.widget().set(PROP_POLICY_LIST, policy_list_name);
}
self.adjust_count(ctx);
}
fn update(&mut self, registry: &mut Registry, ctx: &mut Context) {
// clear focus on focus moved
if self.last_focused != ctx.window().get::<Global>("global").focused_widget {
if let Some(last_focused) = self.last_focused {
ctx.get_widget(last_focused).set("focused", false);
ctx.get_widget(last_focused)
.set("visibility", Visibility::Collapsed);
}
}
if let Some(action) = self.action {
match action {
Action::InputTextChanged(text_box) => {
self.adjust_add_button_enabled(text_box, ctx);
}
Action::NewEntry(entity) => {
if let Some(name) = self.fetch_text(ctx, entity) {
self.new_entry(name, registry, ctx);
}
}
Action::RemoveEntry(index) => {
self.remove_entry(index, registry, ctx);
}
Action::TextChanged(entity, index) => {
let text: String16 = ctx.get_widget(entity).clone("text");
if let Some(header) = ctx
.widget()
.get_mut::<PolicyList>("policy_list")
.get_mut(index)
{
header.name = text.to_string();
}
self.save(registry, ctx);
}
Action::SetEntry(text_box) => {
self.last_focused = Some(text_box);
self.set_entry(text_box, ctx);
}
Action::RemoveFocus(text_box) => {
self.last_focused = None;
ctx.push_event_by_window(FocusEvent::RemoveFocus(text_box));
}
Action::OpenPolicyList(index) => {
self.open_policy_list(index, ctx);
}
}
}
self.action = None;
}
}

View File

@@ -0,0 +1,289 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use orbtk::prelude::*;
use crate::{
constants::*,
data::PolicyList,
//policylist_state::{Action, PolicyListState},
policylist_state::*,
};
//type ListIndex = Option<usize>;
widget!(
/// Dialog selecting a list of policy identifiers.
/// The lists groups a collection of policy codes.
PolicyListView<PolicyListState> {
back_entity: u32,
policy_list: PolicyList,
policy_list_count: usize,
policydata_view: u32
}
);
impl Template for PolicyListView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
// collect the DCES elements of our 'policy lists' view
let items_widget = ItemsWidget::new()
.id(ID_POLICY_LIST_ITEMS_WIDGET)
.v_align("start")
.items_builder(move |ctx, index| {
let mut name = "".to_string();
if let Some(policy_list) = ctx
.get_widget(id)
.get::<PolicyList>(PROP_POLICY_LIST)
.get(index)
{
name = policy_list.name.clone();
}
// plus button: open new policy
let new_list_button = Button::new()
.min_height(48.0)
.class(CLASS_ITEM_BUTTON)
.attach(Grid::column(0))
.attach(Grid::row(0))
.attach(Grid::column_span(1))
.on_click(move |ctx, _| {
ctx.get_mut::<PolicyListState>(id)
.action(Action::OpenPolicyList(index));
true
})
.build(ctx);
let text_block = TextBlock::new()
.foreground(new_list_button)
.margin((14.0, 0.0, 0.0, 0.0))
.v_align("center")
.attach(Grid::column(0))
.text(name)
.element("text-box")
.build(ctx);
let text_box = TextBox::new()
.margin((8.0, 0.0, 0.0, 0.0))
.visibility("collapsed")
.v_align("center")
.water_mark("Insert name of a new policy collection ...")
.attach(Grid::column(0))
.text(text_block)
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyListState>(id)
.action(Action::TextChanged(entity, index));
})
.on_activate(move |ctx, entity| {
ctx.get_mut::<PolicyListState>(id)
.action(Action::RemoveFocus(entity));
})
.build(ctx);
Grid::new()
.height(48.0)
.columns(
Columns::new()
.add("*")
.add(8.0)
.add(32.0)
.add(4.0)
.add(32.0)
.add(8.0)
.build(),
)
.child(new_list_button)
.child(text_box)
.child(text_block)
.child(
// toggle if text_box is focused
ToggleButton::new()
.selected(("focused", text_box))
.class(CLASS_ICON_ONLY)
.attach(Grid::column(2))
.min_size(32.0, 32.0)
.v_align("center")
.build(ctx),
)
.child(
Button::new()
// .selected(("focused", text_box))
.class(CLASS_ICON_ONLY)
.attach(Grid::column(2))
.min_size(32.0, 32.0)
.v_align("center")
.icon(material_icons_font_ttf::MD_ADD)
//.icon(material_icons_font_ttf::MD_EDIT)
//.icon("")
.on_mouse_down(|_, _| true)
.on_click(move |ctx, _| {
ctx.get_mut::<PolicyListState>(id)
.action(Action::SetEntry(text_box));
true
})
.build(ctx),
)
.child(
Button::new()
.class("icon_only")
.attach(Grid::column(4))
.min_size(32.0, 32.0)
.v_align("center")
.icon(material_icons_font_ttf::MD_DELETE)
//.icon("")
.on_mouse_down(|_, _| true)
.on_click(move |ctx, _| {
ctx.get_mut::<PolicyListState>(id)
.action(Action::RemoveEntry(index));
true
})
.build(ctx),
)
.build(ctx)
})
.count((PROP_POLICY_LIST_COUNT, id))
.build(ctx);
// create new policy list element
let policy_list_text_box = TextBox::new()
.id(ID_POLICY_LIST_TEXT_BOX)
.attach(Grid::row(4))
.v_align("center")
.margin((4.0, 0.0, 0.0, 2.0))
.lost_focus_on_activation(false)
.on_activate(move |ctx, entity| {
ctx.get_mut::<PolicyListState>(id)
.action(Action::NewEntry(entity));
})
.on_changed(move |ctx, entity| {
ctx.get_mut::<PolicyListState>(id)
.action(Action::InputTextChanged(entity));
})
.build(ctx);
let scroll_viewer = ScrollViewer::new()
.scroll_viewer_mode(("disabled", "auto"))
.child(items_widget)
.build(ctx);
// Child page: list available policy lists
self.name("PolicyListView")
.policy_list(PolicyList::default())
.policy_list_count(0)
.child(
Grid::new()
.rows(
Rows::new()
.add(52.0)
.add(1.0)
.add("*")
.add(1.0)
.add(40.0)
.build(),
)
.columns(
Columns::new()
.add("*")
.add(4.0)
.add(36.0)
.build(),
)
// Content
.child(
Container::new()
.attach(Grid::row(2))
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.child(scroll_viewer)
.child(
ScrollIndicator::new()
.padding((0.0, 4.0, 0.0, 0.0))
.content_id(items_widget.0)
.scroll_offset(scroll_viewer)
.build(ctx),
)
.build(ctx),
)
// Top Bar
.child(
Container::new()
.class(CLASS_TOP_BAR)
.attach(Grid::row(0))
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.child(
Grid::new()
.child(
TextBlock::new()
.class(CLASS_HEADER)
.v_align("center")
.h_align("center")
.text("Lists of policy data collections")
.build(ctx),
)
.build(ctx),
)
.build(ctx),
)
.child(
Container::new()
.class("separator")
.attach(Grid::row(1))
.attach(Grid::column_span(3))
.build(ctx),
)
.child(
Container::new()
.class("separator")
.attach(Grid::row(3))
.attach(Grid::column_span(3))
.build(ctx),
)
// Bottom bar
.child(
Container::new()
.class(CLASS_BOTTOM_BAR)
.attach(Grid::row(4))
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.build(ctx),
)
.child(
// workaround, todo fix scroll viewer mouse behavior in OrbTk
Button::new()
.attach(Grid::row(4))
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.on_mouse_down(|_, _| true)
.on_mouse_up(|_, _| true)
.on_click(|_, _| true)
.class(CLASS_TRANSPARENT)
.build(ctx),
)
.child(policy_list_text_box)
.child(
Button::new()
.id(ID_POLICY_LIST_ADD_BUTTON)
.class(CLASS_ICON_ONLY)
.attach(Grid::row(4))
.attach(Grid::column(2))
.margin((0.0, 0.0, 4.0, 0.0))
.enabled(false)
.min_size(32.0, 32.0)
.v_align("center")
.icon(material_icons_font_ttf::MD_ADD)
.on_click(move |ctx, _| {
ctx.get_mut::<PolicyListState>(id)
.action(Action::NewEntry(policy_list_text_box));
true
})
.build(ctx),
)
.build(ctx),
)
}
}

1
advotrackerd Submodule

Submodule advotrackerd added at 409d8d1f1a

Submodule backend deleted from 2dbea55fba

Submodule database deleted from 2ce66aef09

View File

@@ -1,28 +0,0 @@
[package]
name = "advotracker_frontend"
version = "0.1.0"
authors = ["Ralf Zerres <ralf.zerres@networkx.de>"]
description = "Frontend component that supports lawyers to capture relevant data encountered during an online legal advice."
readme = "README.md"
license = "(0BSD OR MIT)"
edition = "2018"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winuser"] }
#diesel = { version = "1.4", features = [ "postgres", "sqlite" ] }
[dependencies]
#orbtk = "~0.3.1-alpha3"
orbtk = { git = "https://github.com/redox-os/orbtk.git", branch = "develop" }
serde = { version = "~1.0", features = ["derive"] }
chrono = { version = "~0.4.0", features = ["serde"] }
[features]
# no features by default
default = ["orbtk/debug"]
[package.metadata.bundle]
name = "AdvoTracker"
identifier = "rzerres.advotracker"
short_description = "Task app based on OrbTk."
description = "Supports lawyers to capture relevant data encountered during an online legal advice.\n"

View File

@@ -1,20 +0,0 @@
# OrbTk examples
[![Build and test](https://github.com/redox-os/orbtk/workflows/build/badge.svg)](https://github.com/redox-os/orbtk/actions)
![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)
* calculator: calculator example
* canvas: use third party render library in canvas
* clear: interaction between widgets
* grid: grid layout example
* image: image widget
* minimal: minimal example
* multi_window: multi window example
* overlay: draw widgets on the top
* settings: use registry and settings service (load / save)
* stack: stack layout example
* widgets: widget overview
## License
Licensed under MIT license ([LICENSE](../LICENSE)).

View File

@@ -1,478 +0,0 @@
use std::collections::HashSet;
use orbtk::prelude::*;
#[derive(Debug, Copy, Clone)]
enum Action {
AddItem,
ClearText,
EntryActivated(Entity),
EntryChanged(Entity),
ValueChanged(Entity),
IncrementCounter,
RemoveItem,
}
#[derive(AsAny)]
pub struct MainViewState {
action: Option<Action>,
}
impl Default for MainViewState {
fn default() -> Self {
MainViewState { action: None }
}
}
impl MainViewState {
fn action(&mut self, action: impl Into<Option<Action>>) {
self.action = action.into();
}
}
impl State for MainViewState {
fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
if let Some(action) = self.action {
match action {
Action::AddItem => {
let len = ctx.widget().get::<List>("list").len();
if len < 5 {
ctx.widget()
.get_mut::<List>("list")
.push(format!("Item {}", len + 1));
ctx.child("items").set("count", len + 1);
ctx.child("remove-item-button").set("enabled", true);
if len == 4 {
ctx.child("add-item-button").set("enabled", false);
}
}
}
Action::RemoveItem => {
let len = ctx.widget().get::<List>("list").len();
if len > 0 {
ctx.widget().get_mut::<List>("list").remove(len - 1);
ctx.child("items").set("count", len - 1);
ctx.child("add-item-button").set("enabled", true);
if len == 1 {
ctx.child("remove-item-button").set("enabled", false);
}
}
}
Action::IncrementCounter => {
*ctx.widget().get_mut::<usize>("counter") += 1;
let counter = *ctx.widget().get::<usize>("counter");
ctx.widget().set(
"result",
String16::from(format!("Button count: {}", counter)),
);
}
Action::ClearText => {
ctx.widget().set("text_one", String16::from(""));
ctx.widget().set("text_two", String16::from(""));
}
Action::EntryActivated(entity) => {
let mut widget = ctx.get_widget(entity);
let text = widget.get_mut::<String16>("text");
println!("submitting {}", text);
text.clear();
}
Action::EntryChanged(entity) => {
let widget = ctx.get_widget(entity);
let text = widget.get::<String16>("text");
println!("entry changed: {}", text);
}
Action::ValueChanged(entity) => {
let val =
((*ctx.get_widget(entity).get::<f64>("val")).floor() as i32).to_string();
ctx.child("value_text").set("text", String16::from(val));
}
}
self.action = None;
}
}
fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
let mut selection_string = "Selected:".to_string();
for index in &ctx.widget().get::<SelectedIndices>("selected_indices").0 {
selection_string = format!("{} {}", selection_string, index);
}
ctx.child("selection")
.set("text", String16::from(selection_string));
}
}
fn create_header(ctx: &mut BuildContext, text: &str) -> Entity {
TextBlock::new()
.text(text)
.element("text-block")
.class("h1")
.build(ctx)
}
type List = Vec<String>;
widget!(
MainView<MainViewState> {
selected_indices: SelectedIndices,
counter: usize,
list_count: usize,
combo_box_list_count: usize,
list: List,
selection_list: List,
combo_box_list: List,
selection_list_count: usize,
text_one: String16,
text_two: String16,
result: String16
}
);
impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
self.name("MainView")
.result("Button count: 0")
.counter(0)
.selected_indices(HashSet::new())
.list(vec![
"Item 1".to_string(),
"Item 2".to_string(),
"Item 3".to_string(),
])
.list_count(3)
.selection_list(vec![
"Item 1".to_string(),
"Item 2".to_string(),
"Item 3".to_string(),
"Item 4".to_string(),
"Item 5".to_string(),
"Item 6".to_string(),
"Item 7".to_string(),
"Item 8".to_string(),
"Item 9".to_string(),
"Item 10".to_string(),
])
.combo_box_list(vec![
"CB 1".to_string(),
"CB 2".to_string(),
"CB 3".to_string(),
"CB 4".to_string(),
"CB 5".to_string(),
"CB 6".to_string(),
"CB 7".to_string(),
"CB 8".to_string(),
"CB 9".to_string(),
"CB 10".to_string(),
])
.selection_list_count(10)
.combo_box_list_count(10)
.child(
Grid::new()
.margin(8.0)
.columns(
Columns::new()
.add(132.0)
.add(16.0)
.add(132.0)
.add(16.0)
.add(132.0),
)
.child(
Stack::new()
.attach(Grid::column(0))
// Column 0
.child(create_header(ctx, "Buttons"))
.child(
Button::new()
.text("Button")
.margin((0.0, 8.0, 0.0, 0.0))
.icon(material_font_icons::CHECK_FONT_ICON)
.attach(Grid::column(0))
.attach(Grid::row(1))
.on_click(move |states, _| {
state(id, states).action(Action::IncrementCounter);
true
})
.build(ctx),
)
.child(
Button::new()
.text("Primary")
.element("button")
.class("primary")
.margin((0.0, 8.0, 0.0, 0.0))
.icon(material_font_icons::CHECK_FONT_ICON)
.attach(Grid::column(0))
.attach(Grid::row(2))
.build(ctx),
)
.child(
ToggleButton::new()
.class("single_content")
.text("ToggleButton")
.margin((0.0, 8.0, 0.0, 0.0))
.attach(Grid::column(0))
.attach(Grid::row(3))
.build(ctx),
)
.child(
CheckBox::new()
.text("CheckBox")
.margin((0.0, 8.0, 0.0, 0.0))
.attach(Grid::column(0))
.attach(Grid::row(4))
.build(ctx),
)
.child(
Switch::new()
.margin((0.0, 8.0, 0.0, 0.0))
.attach(Grid::column(0))
.attach(Grid::row(5))
.build(ctx),
)
.child(
TextBlock::new()
.margin((0.0, 8.0, 0.0, 0.0))
.element("h1")
.id("value_text")
.text("0")
.h_align("center")
.build(ctx),
)
.child(
Slider::new()
.on_changed(move |states, entity| {
state(id, states).action(Action::ValueChanged(entity));
})
.build(ctx),
)
.build(ctx),
)
.child(
Stack::new()
.attach(Grid::column(2))
.child(create_header(ctx, "Text"))
.child(
TextBlock::new()
.class("body")
.text(("result", id))
.margin((0.0, 8.0, 0.0, 0.0))
.attach(Grid::column(2))
.attach(Grid::row(1))
.build(ctx),
)
.child(
TextBox::new()
.water_mark("TextBox...")
.text(("text_one", id))
.margin((0.0, 8.0, 0.0, 0.0))
.attach(Grid::column(2))
.attach(Grid::row(2))
.on_activate(move |states, entity| {
state(id, states).action(Action::EntryActivated(entity));
})
.on_changed(move |states, entity| {
state(id, states).action(Action::EntryChanged(entity));
})
.build(ctx),
)
.child(
TextBox::new()
.water_mark("TextBox...")
.text(("text_two", id))
.margin((0.0, 8.0, 0.0, 0.0))
.attach(Grid::column(2))
.attach(Grid::row(2))
.on_activate(move |states, entity| {
state(id, states).action(Action::EntryActivated(entity));
})
.on_changed(move |states, entity| {
state(id, states).action(Action::EntryChanged(entity));
})
.build(ctx),
)
.child(
Button::new()
.margin((0.0, 8.0, 0.0, 0.0))
.class("single_content")
.text("clear text")
.on_click(move |states, _| {
state(id, states).action(Action::ClearText);
true
})
.build(ctx),
)
.child(
NumericBox::new()
.margin((0.0, 8.0, 0.0, 0.0))
.max(123.0)
.step(0.123)
.val(0.123)
.build(ctx),
)
.build(ctx),
)
.child(
Grid::new()
.rows(
Rows::new()
.add("auto")
.add(32.0)
.add(16.0)
.add(204.0)
.add("auto")
.add(192.0)
.add("auto"),
)
.columns(Columns::new().add("*").add(4.0).add("*"))
.attach(Grid::column(4))
.child(
TextBlock::new()
.text("Items")
.element("text-block")
.class("h1")
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.attach(Grid::row(0))
.build(ctx),
)
.child(
ComboBox::new()
.items_builder(move |bc, index| {
let text = bc
.get_widget(id)
.get::<Vec<String>>("combo_box_list")[index]
.clone();
TextBlock::new()
.margin((0.0, 0.0, 0.0, 2.0))
.v_align("center")
.text(text)
.build(bc)
})
.selected_index(0)
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.attach(Grid::row(1))
.margin((0.0, 8.0, 0.0, 0.0))
.count(("combo_box_list_count", id))
.build(ctx),
)
.child(
ItemsWidget::new()
.element("items-widget")
.id("items")
.padding((4.0, 4.0, 4.0, 2.0))
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.attach(Grid::row(3))
.margin((0.0, 0.0, 0.0, 8.0))
.items_builder(move |bc, index| {
let text = bc.get_widget(id).get::<Vec<String>>("list")
[index]
.clone();
Button::new()
.margin((0.0, 0.0, 0.0, 2.0))
.text(text)
.build(bc)
})
.count(("list_count", id))
.build(ctx),
)
.child(
Button::new()
.element("button")
.class("single_content")
.id("remove-item-button")
.icon(material_font_icons::MINUS_FONT_ICON)
.on_click(move |states, _| {
state(id, states).action(Action::RemoveItem);
true
})
.min_width(0.0)
.attach(Grid::column(0))
.attach(Grid::row(4))
.build(ctx),
)
.child(
Button::new()
.element("button")
.class("single_content")
.id("add-item-button")
.icon(material_font_icons::ADD_FONT_ICON)
.on_click(move |states, _| {
state(id, states).action(Action::AddItem);
true
})
.min_width(0.0)
.attach(Grid::column(2))
.attach(Grid::row(4))
.build(ctx),
)
.child(
ListView::new()
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.attach(Grid::row(5))
.selected_indices(id)
.margin((0.0, 16.0, 0.0, 8.0))
.items_builder(move |bc, index| {
let text = bc
.get_widget(id)
.get::<Vec<String>>("selection_list")[index]
.clone();
TextBlock::new()
.margin((0.0, 0.0, 0.0, 2.0))
.v_align("center")
.text(text)
.build(bc)
})
.count(("selection_list_count", id))
.build(ctx),
)
.child(
// todo: wrong text width????
TextBlock::new()
.element("text-block")
.id("selection")
.max_width(120.0)
.attach(Grid::column(0))
.attach(Grid::column_span(3))
.attach(Grid::row(6))
.text("Selected:")
.build(ctx),
)
.build(ctx),
)
.build(ctx),
)
}
}
fn main() {
// use this only if you want to run it as web application.
//orbtk::initialize();
Application::new()
.window(|ctx| {
Window::new()
.title("OrbTk - widgets example")
.position((100.0, 100.0))
.size(468.0, 730.0)
.resizeable(true)
.child(MainView::new().build(ctx))
.build(ctx)
})
.run();
}
// helper to request MainViewState
fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut MainViewState {
states.get_mut(id)
}

View File

@@ -1,32 +0,0 @@
.header {
background: #444e55;
}
.content {
background: #3b434a;
}
text_box {
background: transparent;
border-width: 0;
color: #9dafbf;
font-size: 16;
}
#input {
font-size: 16;
}
text-block {
font-size: 42;
color: #dfebf5;
}
#input {
background: transparent;
}
button {
border-radius: 1;
font-size: 20;
}

View File

@@ -1,15 +0,0 @@
.header {
background: #ffffff;
}
.content {
background: #fafafa;
}
text_box {
color: #4d4c4c;
}
text-block {
color: #4d4c4c;
}

View File

@@ -1,317 +0,0 @@
use orbtk::prelude::*;
use orbtk::theme::DEFAULT_THEME_CSS;
static DARK_EXT: &'static str = include_str!("../resources/calculator-dark.css");
#[cfg(feature = "light-theme")]
static LIGHT_EXT: &'static str = include_str!("../resources/calculator-light.css");
#[cfg(not(feature = "light-theme"))]
fn get_theme() -> ThemeValue {
ThemeValue::create_from_css(DEFAULT_THEME_CSS)
.extension_css(DARK_EXT)
.build()
}
#[cfg(feature = "light-theme")]
fn get_theme() -> ThemeValue {
ThemeValue::create()
.extension_css(DARK_EXT)
.extension_css(LIGHT_EXT)
.build()
}
#[derive(Debug, Copy, Clone)]
enum Action {
Digit(char),
Operator(char),
}
#[derive(Default, AsAny)]
pub struct MainViewState {
input: String,
operator: Option<char>,
left_side: Option<f64>,
right_side: Option<f64>,
action: Option<Action>,
}
impl MainViewState {
fn action(&mut self, action: impl Into<Option<Action>>) {
self.action = action.into();
}
fn calculate(&mut self, ctx: &mut Context) {
let mut result = 0.0;
if let Some(operator) = self.operator {
if let Some(left_side) = self.left_side {
if let Some(right_side) = self.right_side {
match operator {
'+' => {
result = left_side + right_side;
}
'-' => {
result = left_side - right_side;
}
'*' => {
result = left_side * right_side;
}
'/' => {
result = left_side / right_side;
}
_ => {}
}
}
}
}
if result % 1.0 == 0.0 {
ctx.widget()
.set("text", String16::from(format!("{}", result)));
} else {
ctx.widget()
.set("text", String16::from(format!("{:.8}", result)));
}
self.left_side = Some(result);
self.right_side = None;
}
}
impl State for MainViewState {
fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
if let Some(action) = self.action {
match action {
Action::Digit(digit) => {
self.input.push(digit);
ctx.child("input").get_mut::<String16>("text").push(digit);
}
Action::Operator(operator) => match operator {
'C' => {
self.input.clear();
self.left_side = None;
self.operator = None;
self.right_side = None;
ctx.widget().get_mut::<String16>("text").clear();
ctx.child("input").get_mut::<String16>("text").clear()
}
'=' => {
self.right_side = Some(self.input.parse().unwrap_or(0.0));
self.calculate(ctx);
self.input.clear();
self.left_side = None;
self.operator = None;
self.right_side = None;
ctx.child("input").get_mut::<String16>("text").clear()
}
_ => {
if self.input.is_empty() {
return;
}
if self.left_side.is_none() {
self.left_side = Some(self.input.parse().unwrap_or(0.0));
} else {
self.right_side = Some(self.input.parse().unwrap_or(0.0));
self.calculate(ctx);
}
ctx.child("input")
.get_mut::<String16>("text")
.push(operator);
self.input.clear();
self.operator = Some(operator);
}
},
}
self.action = None;
}
}
}
fn generate_digit_button(
ctx: &mut BuildContext,
id: Entity,
sight: char,
primary: bool,
column: usize,
column_span: usize,
row: usize,
) -> Entity {
let mut button = Button::new()
.class("single_content")
.min_size(48.0, 48.0)
.text(sight.to_string())
.on_click(move |states, _| -> bool {
state(id, states).action(Action::Digit(sight));
true
})
.attach(Grid::column(column))
.attach(Grid::row(row))
.attach(Grid::column_span(column_span));
if primary {
button = button.class("primary");
}
button.build(ctx)
}
fn generate_operation_button(
ctx: &mut BuildContext,
id: Entity,
sight: char,
primary: bool,
column: usize,
column_span: usize,
row: usize,
) -> Entity {
let mut button = Button::new()
.class("single_content")
.min_size(48.0, 48.0)
.text(sight.to_string())
.class("square")
.on_click(move |states, _| -> bool {
state(id, states).action(Action::Operator(sight));
true
})
.attach(Grid::column(column))
.attach(Grid::column_span(column_span))
.attach(Grid::row(row));
if primary {
button = button.class("primary");
}
button.build(ctx)
}
widget!(MainView<MainViewState> {
text: String16
});
impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
self.name("MainView")
.width(212.0)
.height(336.0)
.text("")
.child(
Grid::new()
.rows(Rows::new().add(72.0).add("*").build())
.child(
Container::new()
.padding(8.0)
.element("container")
.class("header")
.attach(Grid::row(0))
.child(
Grid::new()
.child(
ScrollViewer::new()
.scroll_viewer_mode(("custom", "disabled"))
.child(
TextBlock::new()
.width(0.0)
.height(14.0)
.text("")
.element("text-block")
.id("input")
.v_align("start")
.build(ctx),
)
.build(ctx),
)
.child(
TextBlock::new()
.element("text-block")
.text(id)
.v_align("end")
.h_align("end")
.build(ctx),
)
.build(ctx),
)
.build(ctx),
)
.child(
Container::new()
.element("container")
.class("content")
.padding(8.0)
.attach(Grid::row(1))
.child(
Grid::new()
.columns(
Columns::new()
.add(48.0)
.add(4.0)
.add(48.0)
.add(4.0)
.add(48.0)
.add(4.0)
.add(48.0)
.build(),
)
.rows(
Rows::new()
.add(48.0)
.add(4.0)
.add(48.0)
.add(4.0)
.add(48.0)
.add(4.0)
.add(48.0)
.add(4.0)
.add(48.0)
.build(),
)
// row 0
.child(generate_operation_button(ctx, id, 'C', false, 0, 5, 0))
.child(generate_operation_button(ctx, id, '/', true, 6, 3, 0))
// row 2
.child(generate_digit_button(ctx, id, '7', false, 0, 1, 2))
.child(generate_digit_button(ctx, id, '8', false, 2, 1, 2))
.child(generate_digit_button(ctx, id, '9', false, 4, 1, 2))
.child(generate_operation_button(ctx, id, '*', true, 6, 1, 2))
// row 4
.child(generate_digit_button(ctx, id, '4', false, 0, 1, 4))
.child(generate_digit_button(ctx, id, '5', false, 2, 1, 4))
.child(generate_digit_button(ctx, id, '6', false, 4, 1, 4))
.child(generate_operation_button(ctx, id, '-', true, 6, 1, 4))
// row 6
.child(generate_digit_button(ctx, id, '1', false, 0, 1, 6))
.child(generate_digit_button(ctx, id, '2', false, 2, 1, 6))
.child(generate_digit_button(ctx, id, '3', false, 4, 1, 6))
.child(generate_operation_button(ctx, id, '+', true, 6, 1, 6))
// row 8
.child(generate_digit_button(ctx, id, '0', false, 0, 3, 8))
.child(generate_digit_button(ctx, id, '.', false, 4, 1, 8))
.child(generate_operation_button(ctx, id, '=', true, 6, 1, 8))
.build(ctx),
)
.build(ctx),
)
.build(ctx),
)
}
}
fn main() {
Application::new()
.window(|ctx| {
Window::new()
.title("OrbTk - Calculator example")
.position((100.0, 100.0))
.size(212.0, 336.0)
.theme(get_theme())
.child(MainView::new().build(ctx))
.build(ctx)
})
.run();
}
// helper to request MainViewState
fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut MainViewState {
states.get_mut(id)
}

View File

@@ -1,279 +0,0 @@
use orbtk::{prelude::*, render::platform::RenderContext2D, utils};
use std::cell::Cell;
use euc::{buffer::Buffer2d, rasterizer, Pipeline};
use vek::*;
// Cube is copied from euc spinning_cube example
// https://github.com/zesterer/euc/blob/master/examples/spinning_cube.rs
struct Cube<'a> {
mvp: Mat4<f32>,
positions: &'a [Vec4<f32>],
}
impl<'a> Pipeline for Cube<'a> {
type Vertex = (usize, Rgba<f32>);
type VsOut = Rgba<f32>;
type Pixel = u32;
#[inline(always)]
fn vert(&self, (v_index, v_color): &Self::Vertex) -> ([f32; 4], Self::VsOut) {
((self.mvp * self.positions[*v_index]).into_array(), *v_color)
}
#[inline(always)]
fn frag(&self, v_color: &Self::VsOut) -> Self::Pixel {
let bytes = v_color.map(|e| (e * 255.0) as u8).into_array();
(bytes[2] as u32) << 0
| (bytes[1] as u32) << 8
| (bytes[0] as u32) << 16
| (bytes[3] as u32) << 24
}
}
#[derive(Clone, Default, PartialEq, Pipeline)]
struct CubePipeline {
spin: Cell<f32>,
}
impl render::RenderPipeline for CubePipeline {
fn draw(&self, render_target: &mut render::RenderTarget) {
let mut color = Buffer2d::new(
[
render_target.width() as usize,
render_target.height() as usize,
],
0,
);
let mut depth = Buffer2d::new(
[
render_target.width() as usize,
render_target.height() as usize,
],
1.0,
);
let mvp = Mat4::perspective_fov_rh_no(
1.3,
render_target.width() as f32,
render_target.height() as f32,
0.01,
100.0,
) * Mat4::translation_3d(Vec3::new(0.0, 0.0, -2.0))
* Mat4::<f32>::scaling_3d(0.4)
* Mat4::rotation_x((self.spin.get() * 0.002) * 8.0)
* Mat4::rotation_y((self.spin.get() as f32 * 0.004).cos() * 4.0)
* Mat4::rotation_z((self.spin.get() as f32 * 0.008).sin() * 2.0);
Cube {
mvp,
positions: &[
Vec4::new(-1.0, -1.0, -1.0, 1.0), // 0
Vec4::new(-1.0, -1.0, 1.0, 1.0), // 1
Vec4::new(-1.0, 1.0, -1.0, 1.0), // 2
Vec4::new(-1.0, 1.0, 1.0, 1.0), // 3
Vec4::new(1.0, -1.0, -1.0, 1.0), // 4
Vec4::new(1.0, -1.0, 1.0, 1.0), // 5
Vec4::new(1.0, 1.0, -1.0, 1.0), // 6
Vec4::new(1.0, 1.0, 1.0, 1.0), // 7
],
}
.draw::<rasterizer::Triangles<_, rasterizer::BackfaceCullingEnabled>, _>(
&[
// -x
(0, Rgba::green()),
(3, Rgba::blue()),
(2, Rgba::red()),
(0, Rgba::green()),
(1, Rgba::red()),
(3, Rgba::blue()),
// +x
(7, Rgba::blue()),
(4, Rgba::green()),
(6, Rgba::red()),
(5, Rgba::red()),
(4, Rgba::green()),
(7, Rgba::blue()),
// -y
(5, Rgba::blue()),
(0, Rgba::red()),
(4, Rgba::green()),
(1, Rgba::green()),
(0, Rgba::red()),
(5, Rgba::blue()),
// +y
(2, Rgba::red()),
(7, Rgba::blue()),
(6, Rgba::green()),
(2, Rgba::red()),
(3, Rgba::green()),
(7, Rgba::blue()),
// -z
(0, Rgba::red()),
(6, Rgba::green()),
(4, Rgba::blue()),
(0, Rgba::red()),
(2, Rgba::blue()),
(6, Rgba::green()),
// +z
(7, Rgba::green()),
(1, Rgba::red()),
(5, Rgba::blue()),
(3, Rgba::blue()),
(1, Rgba::red()),
(7, Rgba::green()),
],
&mut color,
Some(&mut depth),
);
render_target.draw(color.as_ref());
}
}
// OrbTk 2D drawing
#[derive(Clone, Default, PartialEq, Pipeline)]
struct Graphic2DPipeline;
impl render::RenderPipeline for Graphic2DPipeline {
fn draw(&self, render_target: &mut render::RenderTarget) {
let mut render_context =
RenderContext2D::new(render_target.width(), render_target.height());
let width = 120.0;
let height = 120.0;
let x = (render_target.width() - width) / 2.0;
let y = (render_target.height() - height) / 2.0;
// render_context.set_fill_style(utils::Brush::SolidColor(Color::from("#000000")));
render_context.set_fill_style(utils::Brush::LinearGradient {
start: Point::new(x, y),
end: Point::new(x + width, y + height),
stops: vec![
LinearGradientStop {
position: 0.0,
color: Color::from("#0021EB"),
},
LinearGradientStop {
position: 0.5,
color: Color::from("#CE2F24"),
},
LinearGradientStop {
position: 1.0,
color: Color::from("#70EF49"),
},
],
});
render_context.fill_rect(x, y, width, height);
// render_target.draw(render_context.data());
}
}
#[derive(Default, AsAny)]
pub struct MainViewState {
cube_spin: f32,
}
impl MainViewState {
fn spin(&mut self) {
self.cube_spin += 32.0;
}
}
impl State for MainViewState {
fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
if let Some(cube) = ctx
.widget()
.get_mut::<RenderPipeline>("render_pipeline")
.0
.as_any()
.downcast_ref::<CubePipeline>()
{
cube.spin.set(self.cube_spin);
}
}
}
widget!(
MainView<MainViewState> {
render_pipeline: RenderPipeline
}
);
impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
self.name("MainView")
.render_pipeline(RenderPipeline(Box::new(CubePipeline::default())))
.child(
Grid::create()
.rows(
Rows::create()
.row("auto")
.row("*")
.row("auto")
.row("*")
.build(),
)
.child(
TextBlock::create()
.attach(Grid::row(0))
.text("Canvas (render with euc crate)")
.element("text-block")
.class("h1")
.margin(4.0)
.build(ctx),
)
.child(
Canvas::create()
.attach(Grid::row(1))
.render_pipeline(id)
.build(ctx),
)
.child(
Button::create()
.text("spin cube")
.vertical_alignment("end")
.attach(Grid::row(1))
.margin(4.0)
.on_click(move |states, _| {
states.get_mut::<MainViewState>(id).spin();
true
})
.build(ctx),
)
.child(
TextBlock::create()
.attach(Grid::row(2))
.text("Canvas (render with OrbTk)")
.element("text-block")
.class("h1")
.margin(4.0)
.build(ctx),
)
.child(
Canvas::create()
.attach(Grid::row(3))
.render_pipeline(RenderPipeline(Box::new(Graphic2DPipeline::default())))
.build(ctx),
)
.build(ctx),
)
}
}
fn main() {
// use this only if you want to run it as web application.
orbtk::initialize();
Application::new()
.window(|ctx| {
Window::create()
.title("OrbTk - canvas example")
.position((100.0, 100.0))
.size(420.0, 730.0)
.child(MainView::create().build(ctx))
.build(ctx)
})
.run();
}

View File

@@ -1,67 +0,0 @@
use orbtk::prelude::*;
#[derive(Default, AsAny)]
pub struct MainViewState {
clear: bool,
}
impl MainViewState {
// Sets an action the state
fn clear(&mut self) {
self.clear = true;
}
}
impl State for MainViewState {
fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
if self.clear {
// Clears the text property of MainView and because
// of the sharing also the text of the TextBox.
ctx.widget().set("text", String16::from(""));
self.clear = false;
}
}
}
widget!(MainView<MainViewState> {
text: String16
});
impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
self.name("MainView").child(
Stack::create()
.orientation("horizontal")
// By injecting the id of the parent the text property
// is shared between the MainView and the TextBox. This
// means both references the same String16 object.
.child(TextBox::create().height(32.0).text(id).build(ctx))
.child(
Button::create()
.margin((8.0, 0.0, 0.0, 0.0))
// mouse click event handler
.on_click(move |states, _| {
// Calls clear of the state of MainView
states.get_mut::<MainViewState>(id).clear();
true
})
.text("Clear")
.build(ctx),
)
.build(ctx),
)
}
}
fn main() {
Application::new()
.window(|ctx| {
Window::create()
.title("OrbTk - minimal example")
.position((100.0, 100.0))
.size(420.0, 730.0)
.child(MainView::create().margin(4.0).build(ctx))
.build(ctx)
})
.run();
}

View File

@@ -1,111 +0,0 @@
use orbtk::api::widget::StatesContext;
use orbtk::prelude::*;
#[derive(Debug)]
pub enum Event {
Increment(Entity),
}
#[derive(AsAny, Default)]
pub struct AppState {
events: VecDeque<Event>,
pub notes: Entity,
count: u32,
}
//rbtk_api::widget::states_context::StatesContext<'_>
#[derive(Debug, Copy, Clone)]
enum Action {
Increment(usize),
}
#[derive(Default, AsAny)]
pub struct MainViewState {
num: usize,
action: Option<Action>,
}
impl MainViewState {
fn action(&mut self, action: impl Into<Option<Action>>) {
self.action = action.into();
}
}
impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
self.name("MainView")
.counter(0)
.result("Button count: 0")
.child(
Button::create()
.selector(Selector::from("button").id("btn"))
.min_size(100.0, 50.0)
.text(("result", id))
.on_click(move |states, _| -> bool {
state(id, states).action(Action::Increment(10));
true
})
.build(ctx),
)
}
}
impl State for MainViewState {
fn update(&mut self, _: &mut Registry, ctx: &mut Context<'_>) {
if let Some(action) = self.action {
match action {
Action::Increment(digit) => {
*ctx.widget().get_mut::<usize>("counter") += digit as usize;
let counter = *ctx.widget().get::<usize>("counter");
ctx.widget().set(
"result",
String16::from(format!("Button count: {}", counter)),
);
}
_ => {}
}
// Is it possible to get rid of this line ?
self.action = None;
}
if let Some(event) = self.events.pop_front() {
match event {
// Create and append the note to the UI.
Event::Increment(entity) => {
if let Some(copy) = Self::fetch_text(ctx, entity) {
self.append_note(ctx, copy);
let count = self.count + 1;
self.count = count;
if count == 1 {
ctx.widget().set::<f64>("spacing", 12.0);
}
}
}
}
}
}
}
widget!(MainView<MainViewState> {
age:i32,
counter:usize,
result: String16
});
fn main() {
Application::new()
.window(|ctx| {
Window::create()
.title("OrbTk - counter example")
.position((100.0, 100.0))
.size(400.0, 400.0)
.child(MainView::create().build(ctx))
.build(ctx)
})
.run();
}
fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut MainViewState {
states.get_mut(id)
}

Some files were not shown because too many files have changed in this diff Show More