[omega] 1.20.0

This commit is contained in:
Quentin Guidée
2020-07-20 17:09:03 +02:00
889 changed files with 38840 additions and 7531 deletions

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: false

View File

@@ -2,7 +2,25 @@ name: Continuous integration
on: [pull_request, push]
jobs:
build-simulator-android:
# nintendo_3ds:
# runs-on: ubuntu-latest
# steps:
# - run: wget https://github.com/devkitPro/pacman/releases/download/v1.0.2/devkitpro-pacman.amd64.deb -O /tmp/devkitpro-pacman.deb
# - run: yes | sudo dpkg -i /tmp/devkitpro-pacman.deb
# - run: yes | sudo dkp-pacman -Syu --needed devkitARM 3dstools libctru
# - run: echo ::set-env name=DEVKITPRO::/opt/devkitpro
# - run: echo ::set-env name=DEVKITARM::/opt/devkitpro/devkitARM
# - run: echo ::set-env name=PATH::$DEVKITPRO/tools/bin:$DEVKITARM/bin:$PATH
# - uses: actions/checkout@v1
# with:
# submodules: true
# - run: make -j2 PLATFORM=simulator TARGET=3ds
# - uses: actions/upload-artifact@master
# with:
# name: epsilon-3ds.3dsx
# path: output/release/simulator/3ds/epsilon.3dsx
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
@@ -11,9 +29,9 @@ jobs:
- run: make -j2 PLATFORM=simulator TARGET=android
- uses: actions/upload-artifact@master
with:
name: epsilon-simulator-android.apk
path: output/release/simulator/android/app/outputs/apk/release/android-release-unsigned.apk
build-device-n0100:
name: epsilon-android.apk
path: output/release/simulator/android/epsilon.apk
n0100:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
@@ -21,18 +39,40 @@ jobs:
- uses: actions/checkout@v1
with:
submodules: true
- run: make -j2 MODEL=n0100 epsilon.dfu
- run: make -j2 MODEL=n0100 epsilon.onboarding.dfu
- run: make -j2 MODEL=n0100 epsilon.onboarding.update.dfu
- run: make -j2 MODEL=n0100 epsilon.onboarding.beta.dfu
- run: make -j2 MODEL=n0100 flasher.light.dfu
- run: make -j2 MODEL=n0100 flasher.verbose.dfu
- run: mkdir final-output
- run: make -j2 MODEL=n0100 EPSILON_I18N=en output/release/device/n0100/epsilon.onboarding.two_binaries
- run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.en.bin
- run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
- run: make -j2 MODEL=n0100 EPSILON_I18N=fr output/release/device/n0100/epsilon.onboarding.two_binaries
- run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.fr.bin
- run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
- run: make -j2 MODEL=n0100 EPSILON_I18N=nl output/release/device/n0100/epsilon.onboarding.two_binaries
- run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.nl.bin
- run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
- run: make -j2 MODEL=n0100 EPSILON_I18N=pt output/release/device/n0100/epsilon.onboarding.two_binaries
- run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.pt.bin
- run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
- run: make -j2 MODEL=n0100 EPSILON_I18N=it output/release/device/n0100/epsilon.onboarding.two_binaries
- run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.it.bin
- run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
- run: make -j2 MODEL=n0100 EPSILON_I18N=de output/release/device/n0100/epsilon.onboarding.two_binaries
- run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.de.bin
- run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
- run: make -j2 MODEL=n0100 EPSILON_I18N=es output/release/device/n0100/epsilon.onboarding.two_binaries
- run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.es.bin
- run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
- run: make -j2 MODEL=n0100 EPSILON_I18N=hu output/release/device/n0100/epsilon.onboarding.two_binaries
- run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.hu.bin
- run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
- run: make -j2 MODEL=n0100 output/release/device/n0100/flasher.light.bin
- run: mv output/release/device/n0100/flasher.light.bin final-output/flasher.light.bin
- run: find final-output/ -type f -exec bash -c "shasum -a 256 -b {} > {}.sha256" \;
- run: tar cvfz binpack-n0100.tgz final-output/*
- uses: actions/upload-artifact@master
with:
name: epsilon-device-n0100.dfu
path: output/release/device/n0100/epsilon.dfu
- run: make -j2 MODEL=n0100 test.elf
build-device-n0110:
name: epsilon-binpack-n0100.tgz
path: binpack-n0100.tgz
n0110:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
@@ -48,12 +88,13 @@ jobs:
- run: make -j2 flasher.verbose.dfu
- run: make -j2 bench.ram.dfu
- run: make -j2 bench.flash.dfu
- run: make -j2 binpack
- run: cp output/release/device/n0110/binpack-n0110-`git rev-parse HEAD | head -c 7`.tgz output/release/device/n0110/binpack-n0110.tgz
- uses: actions/upload-artifact@master
with:
name: epsilon-device-n0110.dfu
path: output/release/device/n0110/epsilon.dfu
- run: make -j2 test.elf
build-simulator-web:
name: epsilon-binpack-n0110.tgz
path: output/release/device/n0110/binpack-n0110.tgz
web:
runs-on: ubuntu-latest
steps:
- uses: numworks/setup-emscripten@v2
@@ -65,10 +106,9 @@ jobs:
- run: make -j2 PLATFORM=simulator TARGET=web
- uses: actions/upload-artifact@master
with:
name: epsilon-simulator-web.zip
name: epsilon-web.zip
path: output/release/simulator/web/epsilon.zip
- run: make -j2 PLATFORM=simulator TARGET=web test.headless.js
build-simulator-linux:
linux:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
@@ -78,6 +118,6 @@ jobs:
- run: make -j2 PLATFORM=simulator
- uses: actions/upload-artifact@master
with:
name: epsilon-simulator-linux.bin
name: epsilon-linux.bin
path: output/release/simulator/linux/epsilon.bin
- run: make -j2 PLATFORM=simulator test.headless.bin

38
.github/workflows/metric-workflow.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Metrics
on: [pull_request]
jobs:
binary-size:
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
- name: Install ARM toolchain
uses: numworks/setup-arm-toolchain@v1
- name: Checkout PR base
uses: actions/checkout@v2
with:
submodules: recursive
ref: ${{ github.event.pull_request.base.sha }}
path: base
- name: Build base
run: make -j2 -C base epsilon.elf
- name: Checkout PR head
uses: actions/checkout@v2
with:
submodules: recursive
ref: ${{ github.event.pull_request.head.sha }}
path: head
- name: Build head
run: make -j2 -C head epsilon.elf
- name: Retrieve binary size analysis
id: binary_size
run: echo "::set-output name=table::$(python3 head/build/metrics/binary_size.py base/output/release/device/n0110/epsilon.elf head/output/release/device/n0110/epsilon.elf --labels Base Head --sections .text .rodata .bss .data --custom 'Total (RAM)' .data .bss --custom 'Total (ROM)' .text .rodata .data --escape)"
- name: Prepare comment auth
run: echo "::set-env name=GITHUB_TOKEN::$(echo YjgxYTk1YTQ4YzYxNjU4ZTA3YWQzNDYwNTk3ZTI2MTlkODU5MThlOQo= | base64 --decode)"
- name: Add comment
uses: actions/github@v1.0.0
env:
GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }}
with:
args: comment ${{ steps.binary_size.outputs.table }}

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
/build/artifacts/
build/device/**/*.pyc
epsilon.elf
epsilon.map
.vscode
.DS_Store
.gradle

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at getomega.pro@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@@ -1,4 +1,16 @@
# Disable default Make rules
.SUFFIXES:
# Define the default recipe
default:
include build/config.mak
include build/pimp.mak
include build/defaults.mak
include build/platform.$(PLATFORM).mak
include build/toolchain.$(TOOLCHAIN).mak
include build/variants.mak
include build/helpers.mk
ifeq (${MODEL}, n0110)
apps_list = ${EPSILON_APPS}
@@ -10,42 +22,21 @@ ifdef FORCE_EXTERNAL
apps_list = ${EPSILON_APPS}
endif
# Disable default Make rules
.SUFFIXES:
object_for = $(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(1))))
# Define the default recipe
default:
# Define a standard rule helper
# If passed a last parameter value of with_local_version, we also define an
# extra rule that can build source files within the $(BUILD_DIR). This is useful
# for rules that can be applied for intermediate objects (for example, when
# going .png -> .cpp -> .o).
define rule_label
@ echo "$(shell printf "%-8s" $(strip $(1)))$(@:$(BUILD_DIR)/%=%)"
endef
define rule_for
ifeq ($(strip $(5)),with_local_version)
$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3))) | $(if $(findstring official,${MAKECMDGOALS}),official_authorization)
@ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)"
$(Q) $(4)
ifdef HOME_DISPLAY_EXTERNALS
ifneq ($(filter external,$(apps_list)),)
SFLAGS += -DHOME_DISPLAY_EXTERNALS
else
$(warning HOME_DISPLAY_EXTERNALS is set but external isn't included, ignoring flag.)
endif
endif
$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/. $(if $(findstring official,${MAKECMDGOALS}),official_authorization)
@ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)"
$(Q) $(4)
endef
.PHONY: info
info:
@echo "EPSILON_VERSION = $(EPSILON_VERSION)"
@echo "EPSILON_APPS = $(EPSILON_APPS)"
@echo "EPSILON_I18N = $(EPSILON_I18N)"
@echo "OMEGA_THEME = $(OMEGA_THEME)"
@echo "THEME_NAME = $(THEME_NAME)"
@echo "THEME_REPO = $(THEME_REPO)"
@echo "BUILD_DIR = $(BUILD_DIR)"
@echo "PLATFORM" = $(PLATFORM)
@echo "DEBUG" = $(DEBUG)
@@ -78,6 +69,7 @@ help:
@echo " make PLATFORM=simulator TARGET=macos"
@echo " make PLATFORM=simulator TARGET=web"
@echo " make PLATFORM=simulator TARGET=windows"
@echo " make PLATFORM=simulator TARGET=3ds"
.PHONY: doc
doc:
@@ -107,7 +99,9 @@ $(BUILD_DIR)%/.:
# Each sub-Makefile can either add sources to $(%_src) variables or define a
# new executable target. The $(%_src) variables list the sources that can be
# built and linked to executables being generated.
ifndef USE_LIBA
$(error platform.mak should define USE_LIBA)
endif
ifeq ($(USE_LIBA),0)
include liba/Makefile.bridge
else
@@ -126,10 +120,10 @@ include build/struct_layout/Makefile
include build/scenario/Makefile
include quiz/Makefile # Quiz needs to be included at the end
all_src = $(apps_all_src) $(escher_src) $(ion_all_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(ion_target_device_bench_src) $(tests_src)
all_src = $(apps_src) $(escher_src) $(ion_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_device_flasher_src) $(ion_device_bench_src) $(tests_src)
# Make palette.h a dep for every source-file.
# This ensures that the theming engine works correctly.
$(call object_for,$(all_app_src)): $(BUILD_DIR)/escher/palette.h
$(call object_for,$(all_src)): $(BUILD_DIR)/escher/palette.h $(BUILD_DIR)/apps/i18n.h
all_objs = $(call object_for,$(all_src))
.SECONDARY: $(all_objs)
@@ -144,8 +138,7 @@ all_objs = $(call object_for,$(all_src))
include build/targets.mak
# Fill in the default recipe
DEFAULT ?= $(BUILD_DIR)/epsilon.$(EXE)
default: $(DEFAULT)
default: $(firstword $(HANDY_TARGETS)).$(firstword $(HANDY_TARGETS_EXTENSIONS))
# Load standard build rules
include build/rules.mk

View File

@@ -1,10 +1,10 @@
<p align="center"><img src="https://github.com/Omega-Numworks/Omega-Design/blob/master/Omega-Banner.png" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12123721/87953533-75a22380-caab-11ea-8cde-c40291c4a9ae.png" /></p>
<p align="center">
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="cc by-nc-sa 4.0" src="https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-525252.svg?labelColor=292929&logo=creative%20commons&style=for-the-badge" /></a>
<a href="https://github.com/Omega-Numworks/Omega/issues"><img alt="Issues" src="https://img.shields.io/github/issues/Omega-Numworks/Omega.svg?labelColor=292929&logo=git&style=for-the-badge" /></a>
<br/>
<a href="https://discord.gg/hjH3gtd"><img alt="Discord" src="https://img.shields.io/discord/663420259851567114?color=blue&labelColor=292929&label=chat%20-%20discord&logo=discord&style=for-the-badge" /></a>
<a href="https://discord.gg/X2TWhh9"><img alt="Discord" src="https://img.shields.io/discord/663420259851567114?color=blue&labelColor=292929&label=chat%20-%20discord&logo=discord&style=for-the-badge" /></a>
</p>
## About
@@ -19,10 +19,18 @@ Omega is a fork of Numworks' Epsilon, the OS that runs on their calculator, whic
- ~~32 KB Python heap instead of 16 KB~~ Now available on Epsilon `>=13.2.0`!
- And more...
The main new features are listed [here](https://github.com/Omega-Numworks/Omega/wiki/Main-features), and the complete changelog can be found [here](https://github.com/quentinguidee/Omega/wiki/Complete-changelog).
The main new features are listed [here](https://github.com/Omega-Numworks/Omega/wiki/Main-features), and the complete changelog can be found [here](https://github.com/Omega-Numworks/Omega/wiki/Complete-changelog).
## Installation
### Automatic
You can install Omega automatically on our website [here](https://getomega.web.app/) in the "install" page.
<a href="https://getomega.web.app"><p align="center"><img alt="Omega Banner Discord" src="https://user-images.githubusercontent.com/12123721/86352956-e9000480-bc66-11ea-82b7-79fd7e56fa27.png" /></p></a>
### Manual
First of all, follow **step 1** [here](https://www.numworks.com/resources/engineering/software/build/). Then:
<details>
@@ -110,6 +118,28 @@ Also, you can change the number of processes that run in parallel during the bui
</details>
<details>
<summary><b>3DS Simulator</b></summary>
You need devkitPro and devkitARM installed and in your path (instructions [here](https://devkitpro.org/wiki/Getting_Started))
```
git clone --recursive https://github.com/Omega-Numworks/Omega.git
cd Omega
git checkout --recursive omega-dev
make PLATFORM=simulator TARGET=3ds -j
```
You can then put epsilon.3dsx on a SD card to run it from the HBC or use 3dslink to launch it over the network:
```
3dslink output/release/simulator/3ds/epsilon.3dsx -a <3DS' IP ADDRESS>
```
</details>
If you need help, you can join our Discord server here : https://discord.gg/X2TWhh9
<a href="https://discord.gg/X2TWhh9"><p align="center"><img alt="Omega Banner Discord" src="https://user-images.githubusercontent.com/12123721/86287349-54ef5800-bbe8-11ea-80c1-34eb1f93eebd.png" /></p></a>
---
## Contributing
To contribute, please refer to the [Wiki](https://github.com/Omega-Numworks/Omega/wiki/Contributing)
@@ -126,9 +156,7 @@ To contribute, please refer to the [Wiki](https://github.com/Omega-Numworks/Omeg
* [Omega Website](https://github.com/Omega-Numworks/Omega-Website)
* [Omega RPN `APP`](https://github.com/Omega-Numworks/Omega-RPN)
* [Omega Atom `APP`](https://github.com/Omega-Numworks/Omega-Atom)
* [Omega Converter `APP`](https://github.com/Omega-Numworks/Omega-Converter)
* [Omega Design](https://github.com/Omega-Numworks/Omega-Design)
* [Omega CLI Installer `BETA`](https://github.com/Omega-Numworks/Omega-CLI-Installer)
* [Omega App Template `BETA`](https://github.com/Omega-Numworks/Omega-App-Template)
## About Epsilon
@@ -137,15 +165,11 @@ Omega is a fork of Epsilon, a high-performance graphing calculator operating sys
You can try Epsilon straight from your browser in the [online simulator](https://www.numworks.com/simulator/).
## Contributors ✨
Thanks goes to these wonderful people!
<p align="center"><img src="https://github.com/Omega-Numworks/Omega-Design/blob/master/Omega-Contributors.png" /></p>
## License
NumWorks is a registered trademark. Omega is not affiliated with NumWorks.
NumWorks is a registered trademark of NumWorks SAS, 24 Rue Godot de Mauroy, 75009 Paris, France.
Nintendo and Nintendo 3DS are registered trademarks of Nintendo of America Inc, 4600 150th Ave NE, Redmond, WA 98052, USA.
NumWorks SAS and Nintendo of America Inc aren't associated in any shape or form with this project.
* NumWorks Epsilon is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
* Omega is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).

View File

@@ -1,3 +1,4 @@
include apps/helpers.mk
include apps/shared/Makefile
include apps/home/Makefile
include apps/on_boarding/Makefile
@@ -10,8 +11,15 @@ apps =
# (path to the apps header).
$(foreach i,${apps_list},${eval include apps/$(i)/Makefile})
app_src += $(addprefix apps/,\
apps_src += $(addprefix apps/,\
alternate_empty_nested_menu_controller.cpp \
apps_container.cpp \
apps_container_launch_default.cpp:-onboarding \
apps_container_launch_on_boarding.cpp:+onboarding \
apps_container_prompt_beta.cpp:+beta \
apps_container_prompt_none.cpp:-beta \
apps_container_prompt_none.cpp:-update \
apps_container_prompt_update.cpp:+update \
apps_container_storage.cpp \
apps_window.cpp \
backlight_dimming_timer.cpp \
@@ -19,27 +27,21 @@ app_src += $(addprefix apps/,\
battery_view.cpp \
empty_battery_window.cpp \
exam_pop_up_controller.cpp \
exam_mode_configuration_official.cpp:+official \
exam_mode_configuration_non_official.cpp:-official \
global_preferences.cpp \
i18n.py \
lock_view.cpp \
main.cpp \
math_toolbox.cpp \
math_variable_box_controller.cpp \
math_variable_box_empty_controller.cpp \
shift_alpha_lock_view.cpp \
suspend_timer.cpp \
title_bar_view.cpp \
variable_box_controller.cpp \
variable_box_empty_controller.cpp \
)
tests_src += apps/exam_mode_configuration_non_official.cpp
apps_official += apps/exam_mode_configuration_non_official.cpp
apps_non_official += apps/exam_mode_configuration_non_official.cpp
apps_launch_on_boarding_src += apps/apps_container_launch_on_boarding.cpp
apps_launch_default_src += apps/apps_container_launch_default.cpp
apps_prompt_none_src += apps/apps_container_prompt_none.cpp
apps_prompt_beta_src += apps/apps_container_prompt_beta.cpp
apps_prompt_update_src += apps/apps_container_prompt_update.cpp
snapshots_declaration = $(foreach i,$(apps),$(i)::Snapshot m_snapshot$(subst :,,$(i))Snapshot;)
apps_declaration = $(foreach i,$(apps),$(i) m_$(subst :,,$(i));)
@@ -57,33 +59,20 @@ $(call object_for,apps/apps_container_storage.cpp apps/apps_container.cpp apps/m
SFLAGS += -I$(BUILD_DIR)
i18n_files += $(addprefix apps/language_,$(addsuffix .universal.i18n, $(EPSILON_I18N)))
i18n_files += $(addprefix apps/,\
shared.de.i18n\
shared.en.i18n\
shared.es.i18n\
shared.fr.i18n\
shared.pt.i18n\
shared.hu.i18n\
shared.universal.i18n\
toolbox.de.i18n\
toolbox.en.i18n\
toolbox.es.i18n\
toolbox.fr.i18n\
toolbox.pt.i18n\
toolbox.hu.i18n\
variables.de.i18n\
variables.en.i18n\
variables.es.i18n\
variables.fr.i18n\
variables.pt.i18n\
variables.hu.i18n\
)
ifeq ($(EPSILON_GETOPT),1)
i18n_files += $(addprefix apps/language_,$(addsuffix _iso6391.universal.i18n, $(EPSILON_I18N)))
endif
i18n_files += $(call i18n_with_universal_for,shared)
i18n_files += $(call i18n_without_universal_for,toolbox)
i18n_files += $(call i18n_without_universal_for,variables)
$(eval $(call rule_for, \
I18N, \
apps/i18n.cpp, \
$(i18n_files), \
$$(PYTHON) apps/i18n.py --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^ \
$$(PYTHON) apps/i18n.py --codepoints $(code_points) --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^ --generateISO6391locales $$(EPSILON_GETOPT), \
global \
))
@@ -94,36 +83,30 @@ $(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp
$(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png))
all_app_src = $(app_src)(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(apps_official) $(apps_non_official) $(tests_src)
$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/apps/i18n.h
$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h
$(call object_for,$(all_app_src)): $(BUILD_DIR)/apps/i18n.h
$(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h
apps_tests_src = $(app_calculation_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src)
apps_tests_src = $(app_calculation_test_src) $(app_code_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src)
apps_tests_src += $(addprefix apps/,\
alternate_empty_nested_menu_controller.cpp \
global_preferences.cpp \
)
ifeq ($(THEME_REPO),local)
$(foreach img,$(image_list), $(eval $(call rule_for, \
ICON, \
$(img), \
$(addprefix themes/themes/, $(addsuffix .json, $(OMEGA_THEME))), \
$$(PYTHON) themes/themes_manager.py -i $(OMEGA_THEME) $$@ $(BUILD_DIR)/ \
$(addprefix themes/themes/local/, $(addsuffix .json, $(THEME_NAME))), \
$$(PYTHON) themes/themes_manager.py -i $(THEME_REPO) $(THEME_NAME) $$@ $(BUILD_DIR)/, \
global \
)))
# Configure variants
apps_all_src = $(app_src)
apps_all_src += $(apps_official) $(apps_non_official)
apps_all_src += $(apps_launch_default_src) $(apps_launch_on_boarding_src)
apps_all_src += $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src)
apps_default_src = $(app_src) $(apps_non_official) $(apps_launch_default_src) $(apps_prompt_none_src)
apps_official_default_src = $(app_src) $(apps_official) $(apps_launch_default_src) $(apps_prompt_none_src)
apps_onboarding_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_none_src)
apps_official_onboarding_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_none_src)
apps_onboarding_update_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_update_src)
apps_official_onboarding_update_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_update_src)
apps_onboarding_beta_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src)
apps_official_onboarding_beta_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src)
else
$(foreach img,$(image_list), $(eval $(call rule_for, \
ICON, \
$(img), \
$(addsuffix /escher/palette.h, $(BUILD_DIR)), \
$$(PYTHON) themes/themes_manager.py -i $(THEME_REPO) $(THEME_NAME) $$@ $(BUILD_DIR)/, \
global \
)))
endif

View File

@@ -0,0 +1,18 @@
#include "alternate_empty_nested_menu_controller.h"
void AlternateEmptyNestedMenuController::viewDidDisappear() {
if (isDisplayingEmptyController()) {
pop();
}
NestedMenuController::viewDidDisappear();
}
bool AlternateEmptyNestedMenuController::displayEmptyControllerIfNeeded() {
assert(!isDisplayingEmptyController());
// If the content is empty, we push an empty controller.
if (numberOfRows() == 0) {
push(emptyViewController());
return true;
}
return false;
}

View File

@@ -0,0 +1,19 @@
#ifndef APPS_ALTERNATE_EMPTY_NESTED_MENU_CONTROLLER_H
#define APPS_ALTERNATE_EMPTY_NESTED_MENU_CONTROLLER_H
#include <escher/nested_menu_controller.h>
class AlternateEmptyNestedMenuController : public NestedMenuController {
public:
AlternateEmptyNestedMenuController(I18n::Message title) :
NestedMenuController(nullptr, title)
{}
// View Controller
void viewDidDisappear() override;
protected:
virtual ViewController * emptyViewController() = 0;
bool isDisplayingEmptyController() { return StackViewController::depth() == 2; }
bool displayEmptyControllerIfNeeded();
};
#endif

View File

@@ -37,7 +37,7 @@ AppsContainer::AppsContainer() :
m_usbConnectedSnapshot()
{
m_emptyBatteryWindow.setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height), false);
#if __EMSCRIPTEN__
// #if __EMSCRIPTEN__
/* AppsContainer::poincareCircuitBreaker uses Ion::Keyboard::scan(), which
* calls emscripten_sleep. If we set the poincare circuit breaker, we would
* need to whitelist all the methods that might be in the call stack when
@@ -47,9 +47,13 @@ AppsContainer::AppsContainer() :
* quite painy to maintain).
* We just remove the circuit breaker for now.
* TODO: Put the Poincare circuit breaker back on epsilon's web emulator */
#else
/*
* This can be run in Omega, since it uses WebASM.
*/
// #else
Poincare::Expression::SetCircuitBreaker(AppsContainer::poincareCircuitBreaker);
#endif
// #endif
Ion::Storage::sharedStorage()->setDelegate(this);
}
@@ -88,7 +92,7 @@ MathToolbox * AppsContainer::mathToolbox() {
return &m_mathToolbox;
}
VariableBoxController * AppsContainer::variableBoxController() {
MathVariableBoxController * AppsContainer::variableBoxController() {
return &m_variableBoxController;
}
@@ -218,6 +222,10 @@ bool AppsContainer::processEvent(Ion::Events::Event event) {
switchTo(appSnapshotAtIndex(0));
return true;
}
if (event == Ion::Events::ShiftHome) {
switchTo(appSnapshotAtIndex(1));
return true;
}
if (event == Ion::Events::OnOff) {
suspend(true);
return true;

View File

@@ -8,7 +8,7 @@
#include "apps_window.h"
#include "empty_battery_window.h"
#include "math_toolbox.h"
#include "variable_box_controller.h"
#include "math_variable_box_controller.h"
#include "exam_pop_up_controller.h"
#include "exam_pop_up_controller_delegate.h"
#include "battery_timer.h"
@@ -34,9 +34,9 @@ public:
void reset();
Poincare::Context * globalContext();
MathToolbox * mathToolbox();
VariableBoxController * variableBoxController();
MathVariableBoxController * variableBoxController();
void suspend(bool checkIfOnOffKeyReleased = false);
virtual bool dispatchEvent(Ion::Events::Event event) override;
bool dispatchEvent(Ion::Events::Event event) override;
bool switchTo(App::Snapshot * snapshot) override;
void run() override;
bool updateBatteryState();
@@ -70,7 +70,7 @@ private:
EmptyBatteryWindow m_emptyBatteryWindow;
Shared::GlobalContext m_globalContext;
MathToolbox m_mathToolbox;
VariableBoxController m_variableBoxController;
MathVariableBoxController m_variableBoxController;
ExamPopUpController m_examPopUpController;
OnBoarding::PopUpController m_promptController;
BatteryTimer m_batteryTimer;

View File

@@ -21,6 +21,7 @@ app_calculation_src = $(addprefix apps/calculation/,\
additional_outputs/trigonometry_graph_cell.cpp \
additional_outputs/trigonometry_list_controller.cpp \
additional_outputs/trigonometry_model.cpp \
additional_outputs/unit_list_controller.cpp \
app.cpp \
edit_expression_controller.cpp \
expression_field.cpp \
@@ -30,16 +31,9 @@ app_calculation_src = $(addprefix apps/calculation/,\
)
app_calculation_src += $(app_calculation_test_src)
app_src += $(app_calculation_src)
apps_src += $(app_calculation_src)
i18n_files += $(addprefix apps/calculation/,\
base.de.i18n\
base.en.i18n\
base.es.i18n\
base.fr.i18n\
base.pt.i18n\
base.hu.i18n\
)
i18n_files += $(call i18n_without_universal_for,calculation/base)
tests_src += $(addprefix apps/calculation/test/,\
calculation_store.cpp\

View File

@@ -1,4 +1,5 @@
#include "complex_graph_cell.h"
#include <escher/palette.h>
using namespace Shared;
using namespace Poincare;
@@ -12,7 +13,7 @@ ComplexGraphView::ComplexGraphView(ComplexModel * complexModel) :
}
void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(rect, KDColorWhite);
ctx->fillRect(rect, Palette::BackgroundApps);
// Draw grid, axes and graduations
drawGrid(ctx, rect);
@@ -25,7 +26,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
assert(!std::isnan(real) && !std::isnan(imag) && !std::isinf(real) && !std::isinf(imag));
// Draw the segment from the origin to the dot (real, imag)
drawSegment(ctx, rect, 0.0f, 0.0f, m_complex->real(), m_complex->imag(), Palette::GreyDark, false);
drawSegment(ctx, rect, 0.0f, 0.0f, m_complex->real(), m_complex->imag(), Palette::SecondaryText, false);
/* Draw the partial ellipse indicating the angle θ
* - the ellipse parameters are a = |real|/5 and b = |imag|/5,
@@ -39,7 +40,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
* and the line of equation (real*t,imag*t).
* (a*cos(t), b*sin(t)) = (real*t,imag*t) --> tan(t) = sign(a)*sign(b) (± π)
* --> t = π/4 [π/2] according to sign(a) and sign(b). */
float th = real < 0.0f ? 3.0f*M_PI/4.0f : M_PI/4.0f;
float th = real < 0.0f ? (float)(3.0*M_PI_4) : (float)M_PI_4;
th = imag < 0.0f ? -th : th;
// Compute ellipsis parameters a and b
float factor = 5.0f;
@@ -48,7 +49,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
// Avoid flat ellipsis for edge cases (for real = 0, the case imag = 0 is excluded)
if (real == 0.0f) {
a = 1.0f/factor;
th = imag < 0.0f ? -M_PI/2.0f : M_PI/2.0f;
th = imag < 0.0f ? (float)-M_PI_2 : (float)M_PI_2;
}
std::complex<float> parameters(a,b);
drawCurve(ctx, rect, 0.0f, 1.0f, 0.01f,
@@ -58,27 +59,27 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
float a = parameters.real();
float b = parameters.imag();
return Poincare::Coordinate2D<float>(a*std::cos(t*th), b*std::sin(t*th));
}, &parameters, &th, false, Palette::GreyDark, false);
}, &parameters, &th, false, Palette::SecondaryText, false);
// Draw dashed segment to indicate real and imaginary
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, real, 0.0f, imag, Palette::Red, 1, 3);
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Horizontal, imag, 0.0f, real, Palette::Red, 1, 3);
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, real, 0.0f, imag, Palette::CalculationTrigoAndComplexForeground, 1, 3);
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Horizontal, imag, 0.0f, real, Palette::CalculationTrigoAndComplexForeground, 1, 3);
// Draw complex position on the plan
drawDot(ctx, rect, real, imag, Palette::Red, Size::Large);
drawDot(ctx, rect, real, imag, Palette::CalculationTrigoAndComplexForeground, Size::Large);
// Draw labels
// 're(z)' label
drawLabel(ctx, rect, real, 0.0f, "re(z)", Palette::Red, CurveView::RelativePosition::None, imag >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
drawLabel(ctx, rect, real, 0.0f, "re(z)", Palette::CalculationTrigoAndComplexForeground, CurveView::RelativePosition::None, imag >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
// 'im(z)' label
drawLabel(ctx, rect, 0.0f, imag, "im(θ)", Palette::Red, real >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
drawLabel(ctx, rect, 0.0f, imag, "im(z)", Palette::CalculationTrigoAndComplexForeground, real >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
// '|z|' label, the relative horizontal position of this label depends on the quadrant
CurveView::RelativePosition verticalPosition = real*imag < 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After;
if (real == 0.0f) {
// Edge case: pure imaginary
verticalPosition = CurveView::RelativePosition::None;
}
drawLabel(ctx, rect, real/2.0f, imag/2.0f, "|z|", Palette::Red, CurveView::RelativePosition::None, verticalPosition);
drawLabel(ctx, rect, real/2.0f, imag/2.0f, "|z|", Palette::CalculationTrigoAndComplexForeground, CurveView::RelativePosition::None, verticalPosition);
// 'arg(z)' label, the absolute and relative horizontal/vertical positions of this label depends on the quadrant
CurveView::RelativePosition horizontalPosition = real >= 0.0f ? CurveView::RelativePosition::After : CurveView::RelativePosition::None;
verticalPosition = imag >= 0.0f ? CurveView::RelativePosition::After : CurveView::RelativePosition::Before;
@@ -87,7 +88,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
* and for the left half plan, we position the label at the half angle. The
* relative position is chosen accordingly. */
float anglePositionRatio = real >= 0.0f ? 0.0f : 0.5f;
drawLabel(ctx, rect, a*std::cos(anglePositionRatio*th), b*std::sin(anglePositionRatio*th), "arg(z)", Palette::Red, horizontalPosition, verticalPosition);
drawLabel(ctx, rect, a*std::cos(anglePositionRatio*th), b*std::sin(anglePositionRatio*th), "arg(z)", Palette::CalculationTrigoAndComplexForeground, horizontalPosition, verticalPosition);
}
}

View File

@@ -10,7 +10,7 @@ namespace Calculation {
class ComplexListController : public IllustratedListController {
public:
ComplexListController(EditExpressionController * editExpressionController) :
IllustratedListController(nullptr, editExpressionController),
IllustratedListController(editExpressionController),
m_complexGraphCell(&m_model) {}
// ViewController

View File

@@ -17,10 +17,10 @@ float ComplexModel::rangeBound(float direction, bool horizontal) const {
maxFactor = k_maxHorizontalMarginFactor;
value = real();
}
if (std::isnan(value) || std::isinf(value) || value == 0.0f) {
return direction*maxFactor;
}
float factor = direction*value >= 0.0f ? maxFactor : minFactor;
if (std::isnan(value) || std::isinf(value) || value == 0.0f) {
return direction*factor;
}
return factor*value;
}

View File

@@ -2,6 +2,7 @@
#define CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_MODEL_H
#include "../../shared/curve_view_range.h"
#include "illustrated_list_controller.h"
#include <complex>
namespace Calculation {
@@ -17,11 +18,53 @@ public:
void setComplex(std::complex<float> c) { *this = ComplexModel(c); }
static constexpr float k_minVerticalMarginFactor = -0.5f;
static constexpr float k_maxVerticalMarginFactor = 1.2f;
/* The range is computed from these criteria:
* - The real part is centered horizontally
* - Both left and right margins are equal to the real length
* - The imaginary part is the same length as the real part
* - The remaining vertical margin are splitted as one third at the top, 2
* thirds at the bottom
*
* | | 1/3 * vertical_margin
* +----------+
* | / | |
* | / | | Imaginary
* | / | |
* | / | |
* ----------+----------+----------
* |
* | 2/3 * vertical_margin
* -----------
* Real
*
*/
// Horizontal range
static constexpr float k_minHorizontalMarginFactor = -1.0f;
static constexpr float k_maxHorizontalMarginFactor = 2.0f;
// Vertical range
static constexpr KDCoordinate k_width = Ion::Display::Width - Metric::PopUpRightMargin - Metric::PopUpLeftMargin;
static constexpr KDCoordinate k_height = IllustratedListController::k_illustrationHeight;
static constexpr KDCoordinate k_unit = k_width/3;
/*
* VerticalMaring = k_height - k_unit
*
* Values | Coordinates
* --------+----------------------------------
* imag | k_unit
* Ymax | k_unit + (1/3)*VerticalMargin
* Ymin | -(2/3)*VerticalMargin
*
* Thus:
* Ymin = -(2/3)*k_verticalMargin*imag/k_unit
* = -(2/3)*(k_height/k_unit - 1)*imag
* = 2/3*(1 - k_height/k_unit)*imag
* Ymax = (k_unit + (1/3)*VerticalMargin)*imag/k_unit
* = (1 + (1/3)*(k_height/k_unit - 1))*imag
* = 1/3*(2 + k_height/k_unit)*imag
*
* */
static constexpr float k_minVerticalMarginFactor = 2.0f/3.0f*(1.0f - (float)k_height/(float)k_unit);
static constexpr float k_maxVerticalMarginFactor = 1.0f/3.0f*(2.0f + (float)k_height/(float)k_unit);
private:
float rangeBound(float direction, bool horizontal) const;

View File

@@ -4,13 +4,14 @@
#include <escher.h>
#include <apps/i18n.h>
#include <poincare/layout.h>
#include <escher/palette.h>
namespace Calculation {
class ExpressionWithEqualSignView : public ExpressionView {
public:
ExpressionWithEqualSignView() :
m_equalSign(KDFont::LargeFont, I18n::Message::Equal, 0.5f, 0.5f, KDColorBlack)
m_equalSign(KDFont::LargeFont, I18n::Message::Equal, 0.5f, 0.5f, Palette::PrimaryText)
{}
KDSize minimalSizeForOptimalDisplay() const override;
void drawRect(KDContext * ctx, KDRect rect) const override;

View File

@@ -7,8 +7,8 @@ namespace Calculation {
/* Expressions list controller */
ExpressionsListController::ExpressionsListController(Responder * parentResponder, EditExpressionController * editExpressionController) :
ListController(parentResponder, editExpressionController),
ExpressionsListController::ExpressionsListController(EditExpressionController * editExpressionController) :
ListController(editExpressionController),
m_cells{}
{
for (int i = 0; i < k_maxNumberOfCells; i++) {
@@ -38,9 +38,7 @@ HighlightCell * ExpressionsListController::reusableCell(int index, int type) {
KDCoordinate ExpressionsListController::rowHeight(int j) {
Layout l = layoutAtIndex(j);
if (l.isUninitialized()) {
return 0;
}
assert(!l.isUninitialized());
return l.layoutSize().height() + 2 * Metric::CommonSmallMargin + Metric::CellSeparatorThickness;
}

View File

@@ -10,7 +10,7 @@ namespace Calculation {
class ExpressionsListController : public ListController {
public:
ExpressionsListController(Responder * parentResponder, EditExpressionController * editExpressionController);
ExpressionsListController(EditExpressionController * editExpressionController);
// Responder
void viewDidDisappear() override;
@@ -28,7 +28,7 @@ public:
protected:
constexpr static int k_maxNumberOfCells = 4;
virtual int textAtIndex(char * buffer, size_t bufferSize, int index) override;
int textAtIndex(char * buffer, size_t bufferSize, int index) override;
Poincare::Expression m_expression;
// Memoization of layouts
mutable Poincare::Layout m_layouts[k_maxNumberOfCells];

View File

@@ -1,4 +1,5 @@
#include "illustrated_list_controller.h"
#include <poincare/exception_checkpoint.h>
#include <poincare/symbol.h>
#include "../app.h"
@@ -8,8 +9,8 @@ namespace Calculation {
/* Illustrated list controller */
IllustratedListController::IllustratedListController(Responder * parentResponder, EditExpressionController * editExpressionController) :
ListController(parentResponder, editExpressionController, this),
IllustratedListController::IllustratedListController(EditExpressionController * editExpressionController) :
ListController(editExpressionController, this),
m_additionalCalculationCells{}
{
for (int i = 0; i < k_maxNumberOfAdditionalCalculations; i++) {
@@ -78,7 +79,17 @@ KDCoordinate IllustratedListController::rowHeight(int j) {
return 0;
}
Shared::ExpiringPointer<Calculation> calculation = m_calculationStore.calculationAtIndex(calculationIndex);
return calculation->height(App::app()->localContext(), true, true) + 2 * Metric::CommonSmallMargin + Metric::CellSeparatorThickness;
constexpr bool expanded = true;
KDCoordinate result = calculation->memoizedHeight(expanded);
if (result < 0) {
result = ScrollableThreeExpressionsCell::Height(calculation.pointer());
if (result < 0) {
// Raise, because Height modified the calculation and failed.
Poincare::ExceptionCheckpoint::Raise();
}
calculation->setMemoizedHeight(expanded, result);
}
return result + Metric::CellSeparatorThickness;
}
int IllustratedListController::typeAtLocation(int i, int j) {

View File

@@ -10,8 +10,10 @@
namespace Calculation {
class IllustratedListController : public ListController, public SelectableTableViewDelegate {
/* TODO There is factorizable code between this and
* Calculation::HistoryController (at least rowHeight). */
public:
IllustratedListController(Responder * parentResponder, EditExpressionController * editExpressionController);
IllustratedListController(EditExpressionController * editExpressionController);
// Responder
void viewDidDisappear() override;
@@ -31,7 +33,7 @@ public:
// IllustratedListController
void setExpression(Poincare::Expression e) override;
constexpr static KDCoordinate k_illustrationHeight = 100;
constexpr static KDCoordinate k_illustrationHeight = 120;
protected:
Poincare::Expression m_savedExpression;
CalculationStore m_calculationStore;

View File

@@ -10,7 +10,7 @@ void IllustrationCell::layoutSubviews(bool force) {
}
void IllustrationCell::drawRect(KDContext * ctx, KDRect rect) const {
drawBorderOfRect(ctx, bounds(), Palette::GreyBright);
drawBorderOfRect(ctx, bounds(), Palette::ListCellBorder);
}
}

View File

@@ -28,9 +28,6 @@ Integer::Base baseAtIndex(int index) {
}
void IntegerListController::computeLayoutAtIndex(int index) {
if (!m_layouts[index].isUninitialized()) {
return;
}
assert(m_expression.type() == ExpressionNode::Type::BasedInteger);
// For index = k_indexOfFactorExpression, the layout is assumed to be alreday memoized because it is needed to compute the numberOfRows
assert(index < k_indexOfFactorExpression);
@@ -65,6 +62,6 @@ bool IntegerListController::factorExpressionIsComputable() const {
}
m_layouts[k_indexOfFactorExpression] = EmptyLayout::Builder();
return false;
}
}
}

View File

@@ -8,7 +8,7 @@ namespace Calculation {
class IntegerListController : public ExpressionsListController {
public:
IntegerListController(EditExpressionController * editExpressionController) :
ExpressionsListController(nullptr, editExpressionController) {}
ExpressionsListController(editExpressionController) {}
//ListViewDataSource
int numberOfRows() const override;

View File

@@ -21,8 +21,8 @@ void ListController::InnerListController::didBecomeFirstResponder() {
/* List Controller */
ListController::ListController(Responder * parentResponder, EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate) :
StackViewController(parentResponder, &m_listController, Palette::ToolboxHeaderText, Palette::ToolboxHeaderBackground, Palette::ToolboxHeaderBorder),
ListController::ListController(EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate) :
StackViewController(nullptr, &m_listController, Palette::ToolboxHeaderText, Palette::ToolboxHeaderBackground, Palette::ToolboxHeaderBorder),
m_listController(this, delegate),
m_editExpressionController(editExpressionController)
{
@@ -38,7 +38,6 @@ bool ListController::handleEvent(Ion::Events::Event event) {
* insertTextBody. */
Container::activeApp()->dismissModalViewController();
m_editExpressionController->insertTextBody(buffer);
Container::activeApp()->setFirstResponder(m_editExpressionController);
return true;
}
return false;

View File

@@ -10,7 +10,7 @@ class EditExpressionController;
class ListController : public StackViewController, public ListViewDataSource, public SelectableTableViewDataSource {
public:
ListController(Responder * parentResponder, EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate = nullptr);
ListController(EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate = nullptr);
// Responder
bool handleEvent(Ion::Events::Event event) override;

View File

@@ -8,7 +8,7 @@ namespace Calculation {
class RationalListController : public ExpressionsListController {
public:
RationalListController(EditExpressionController * editExpressionController) :
ExpressionsListController(nullptr, editExpressionController) {}
ExpressionsListController(editExpressionController) {}
//ListViewDataSource
int numberOfRows() const override;

View File

@@ -8,7 +8,8 @@ void ScrollableThreeExpressionsView::resetMemoization() {
setLayouts(Poincare::Layout(), Poincare::Layout(), Poincare::Layout());
}
void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation, bool * didForceOutput) {
assert(!didForceOutput || *didForceOutput == false);
Poincare::Context * context = App::app()->localContext();
// Clean the layouts to make room in the pool
@@ -27,6 +28,9 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
Poincare::ExceptionCheckpoint::Raise();
} else {
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
if (didForceOutput) {
*didForceOutput = true;
}
}
}
}
@@ -46,6 +50,9 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
/* Set the display output to ApproximateOnly, make room in the pool by
* erasing the exact layout, and retry to create the approximate layout */
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
if (didForceOutput) {
*didForceOutput = true;
}
exactOutputLayout = Poincare::Layout();
couldNotCreateApproximateLayout = false;
approximateOutputLayout = calculation->createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
@@ -65,6 +72,27 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
layoutSubviews();
}
KDCoordinate ScrollableThreeExpressionsCell::Height(Calculation * calculation) {
ScrollableThreeExpressionsCell cell;
bool didForceOutput = false;
cell.setCalculation(calculation, &didForceOutput);
if (didForceOutput) {
/* We could not compute the height of the calculation as it is (the display
* output was forced to another value during the height computation).
* Warning: the display output of calculation was actually changed, so it
* will cause problems if we already did some computations with another
* display value. */
return -1;
}
KDRect leftFrame = KDRectZero;
KDRect centerFrame = KDRectZero;
KDRect approximateSignFrame = KDRectZero;
KDRect rightFrame = KDRectZero;
cell.subviewFrames(&leftFrame, &centerFrame, &approximateSignFrame, &rightFrame);
KDRect unionedFrame = leftFrame.unionedWith(centerFrame).unionedWith(rightFrame);
return unionedFrame.height() + 2 * ScrollableThreeExpressionsView::k_margin;
}
void ScrollableThreeExpressionsCell::didBecomeFirstResponder() {
reinitSelection();
Container::activeApp()->setFirstResponder(&m_view);
@@ -75,8 +103,8 @@ void ScrollableThreeExpressionsCell::reinitSelection() {
m_view.reloadScroll();
}
void ScrollableThreeExpressionsCell::setCalculation(Calculation * calculation) {
m_view.setCalculation(calculation);
void ScrollableThreeExpressionsCell::setCalculation(Calculation * calculation, bool * didForceOutput) {
m_view.setCalculation(calculation, didForceOutput);
layoutSubviews();
}

View File

@@ -5,22 +5,30 @@
#include "../../shared/scrollable_multiple_expressions_view.h"
#include "../calculation.h"
#include "expression_with_equal_sign_view.h"
#include <escher/palette.h>
namespace Calculation {
/* TODO There is factorizable code between this and Calculation::HistoryViewCell
* (at least setCalculation). */
class ScrollableThreeExpressionsView : public Shared::AbstractScrollableMultipleExpressionsView {
public:
static constexpr KDCoordinate k_margin = Metric::CommonSmallMargin;
ScrollableThreeExpressionsView(Responder * parentResponder) : Shared::AbstractScrollableMultipleExpressionsView(parentResponder, &m_contentCell), m_contentCell() {
setMargins(Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin); // Left Right margins are already added by TableCell
setBackgroundColor(KDColorWhite);
setMargins(k_margin, k_margin, k_margin, k_margin); // Left Right margins are already added by TableCell
setBackgroundColor(Palette::BackgroundApps);
}
void resetMemoization();
void setCalculation(Calculation * calculation);
void setCalculation(Calculation * calculation, bool * didForceOutput = nullptr);
void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
return m_contentCell.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame);
}
private:
class ContentCell : public Shared::AbstractScrollableMultipleExpressionsView::ContentCell {
public:
ContentCell() : m_leftExpressionView() {}
KDColor backgroundColor() const override { return KDColorWhite; }
KDColor backgroundColor() const override { return Palette::BackgroundApps; }
void setEven(bool even) override { return; }
ExpressionView * leftExpressionView() const override { return const_cast<ExpressionWithEqualSignView *>(&m_leftExpressionView); }
private:
@@ -28,12 +36,13 @@ private:
};
ContentCell * contentCell() override { return &m_contentCell; };
const ContentCell * constContentCell() const override { return &m_contentCell; };
const ContentCell * constContentCell() const override { return &m_contentCell; };
ContentCell m_contentCell;
};
class ScrollableThreeExpressionsCell : public TableCell, public Responder {
public:
static KDCoordinate Height(Calculation * calculation);
ScrollableThreeExpressionsCell() :
Responder(nullptr),
m_view(this) {}
@@ -52,12 +61,15 @@ public:
void setHighlighted(bool highlight) override { m_view.evenOddCell()->setHighlighted(highlight); }
void resetMemoization() { m_view.resetMemoization(); }
void setCalculation(Calculation * calculation);
void setCalculation(Calculation * calculation, bool * didForceOutput = nullptr);
void setDisplayCenter(bool display);
ScrollableThreeExpressionsView::SubviewPosition selectedSubviewPosition() { return m_view.selectedSubviewPosition(); }
void setSelectedSubviewPosition(ScrollableThreeExpressionsView::SubviewPosition subviewPosition) { m_view.setSelectedSubviewPosition(subviewPosition); }
void reinitSelection();
void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
return m_view.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame);
}
private:
// Remove label margin added by TableCell because they're already handled by ScrollableThreeExpressionsView
KDCoordinate labelMargin() const override { return 0; }

View File

@@ -1,4 +1,5 @@
#include "trigonometry_graph_cell.h"
#include <escher/palette.h>
using namespace Shared;
using namespace Poincare;
@@ -14,24 +15,24 @@ TrigonometryGraphView::TrigonometryGraphView(TrigonometryModel * model) :
void TrigonometryGraphView::drawRect(KDContext * ctx, KDRect rect) const {
float s = std::sin(m_model->angle());
float c = std::cos(m_model->angle());
ctx->fillRect(rect, KDColorWhite);
ctx->fillRect(rect, Palette::BackgroundApps);
drawGrid(ctx, rect);
drawAxes(ctx, rect);
// Draw the circle
drawCurve(ctx, rect, 0.0f, 2.0f*M_PI, M_PI/180.0f, [](float t, void * model, void * context) {
return Poincare::Coordinate2D<float>(std::cos(t), std::sin(t));
}, nullptr, nullptr, true, Palette::GreyDark, false);
}, nullptr, nullptr, true, Palette::SecondaryText, false);
// Draw dashed segment to indicate sine and cosine
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, c, 0.0f, s, Palette::Red, 1, 3);
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Horizontal, s, 0.0f, c, Palette::Red, 1, 3);
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, c, 0.0f, s, Palette::CalculationTrigoAndComplexForeground, 1, 3);
drawHorizontalOrVerticalSegment(ctx, rect, Axis::Horizontal, s, 0.0f, c, Palette::CalculationTrigoAndComplexForeground, 1, 3);
// Draw angle position on the circle
drawDot(ctx, rect, c, s, Palette::Red, Size::Large);
drawDot(ctx, rect, c, s, Palette::CalculationTrigoAndComplexForeground, Size::Large);
// Draw graduations
drawLabelsAndGraduations(ctx, rect, Axis::Vertical, false, true);
drawLabelsAndGraduations(ctx, rect, Axis::Horizontal, false, true);
// Draw labels
drawLabel(ctx, rect, 0.0f, s, "sin(θ)", Palette::Red, c >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
drawLabel(ctx, rect, c, 0.0f, "cos(θ)", Palette::Red, CurveView::RelativePosition::None, s >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
drawLabel(ctx, rect, 0.0f, s, "sin(θ)", Palette::CalculationTrigoAndComplexForeground, c >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
drawLabel(ctx, rect, c, 0.0f, "cos(θ)", Palette::CalculationTrigoAndComplexForeground, CurveView::RelativePosition::None, s >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
}
}

View File

@@ -10,7 +10,7 @@ namespace Calculation {
class TrigonometryListController : public IllustratedListController {
public:
TrigonometryListController(EditExpressionController * editExpressionController) :
IllustratedListController(nullptr, editExpressionController),
IllustratedListController(editExpressionController),
m_graphCell(&m_model) {}
void setExpression(Poincare::Expression e) override;
private:

View File

@@ -18,7 +18,7 @@ public:
float yMax() const override { return yCenter() + yHalfRange(); }
void setAngle(float f) { m_angle = f; }
float angle() const { return m_angle*M_PI/Poincare::Trigonometry::PiInAngleUnit(Poincare::Preferences::sharedPreferences()->angleUnit()); }
float angle() const { return m_angle*(float)M_PI/(float)Poincare::Trigonometry::PiInAngleUnit(Poincare::Preferences::sharedPreferences()->angleUnit()); }
private:
constexpr static float k_xHalfRange = 2.1f;
// We center the yRange around the semi-circle where the angle is

View File

@@ -0,0 +1,155 @@
#include "unit_list_controller.h"
#include "../app.h"
#include "../../shared/poincare_helpers.h"
#include <poincare/unit_convert.h>
#include <poincare/multiplication.h>
#include <poincare/power.h>
#include <poincare/undefined.h>
#include <poincare/unit.h>
using namespace Poincare;
using namespace Shared;
namespace Calculation {
void UnitListController::setExpression(Poincare::Expression e) {
ExpressionsListController::setExpression(e);
assert(!m_expression.isUninitialized());
// Reinitialize m_memoizedExpressions
for (size_t i = 0; i < k_maxNumberOfCells; i++) {
m_memoizedExpressions[i] = Expression();
}
size_t numberOfMemoizedExpressions = 0;
// 1. First rows: miscellaneous classic units for some dimensions
Expression copy = m_expression.clone();
Expression units;
// Reduce to be able to recognize units
PoincareHelpers::Reduce(&copy, App::app()->localContext(), ExpressionNode::ReductionTarget::User);
copy = copy.removeUnit(&units);
bool requireSimplification = false;
bool canChangeUnitPrefix = false;
if (Unit::IsSISpeed(units)) {
// 1.a. Turn speed into km/h
m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
m_expression.clone(),
Multiplication::Builder(
Unit::Kilometer(),
Power::Builder(
Unit::Hour(),
Rational::Builder(-1)
)
)
);
requireSimplification = true; // Simplify the conversion
} else if (Unit::IsSIVolume(units)) {
// 1.b. Turn volume into L
m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
m_expression.clone(),
Unit::Liter()
);
requireSimplification = true; // Simplify the conversion
canChangeUnitPrefix = true; // Pick best prefix (mL)
} else if (Unit::IsSIEnergy(units)) {
// 1.c. Turn energy into Wh
m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
m_expression.clone(),
Multiplication::Builder(
Unit::Watt(),
Unit::Hour()
)
);
m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
m_expression.clone(),
Unit::ElectronVolt()
);
requireSimplification = true; // Simplify the conversion
canChangeUnitPrefix = true; // Pick best prefix (kWh)
} else if (Unit::IsSITime(units)) {
// Turn time into ? year + ? month + ? day + ? h + ? min + ? s
double value = Shared::PoincareHelpers::ApproximateToScalar<double>(copy, App::app()->localContext());
m_memoizedExpressions[numberOfMemoizedExpressions++] = Unit::BuildTimeSplit(value, App::app()->localContext(), Preferences::sharedPreferences()->complexFormat(), Preferences::sharedPreferences()->angleUnit());
}
// 1.d. Simplify and tune prefix of all computed expressions
size_t currentExpressionIndex = 0;
while (currentExpressionIndex < numberOfMemoizedExpressions) {
assert(!m_memoizedExpressions[currentExpressionIndex].isUninitialized());
if (requireSimplification) {
Shared::PoincareHelpers::Simplify(&m_memoizedExpressions[currentExpressionIndex], App::app()->localContext(), ExpressionNode::ReductionTarget::User);
}
if (canChangeUnitPrefix) {
Expression newUnits;
// Reduce to be able to removeUnit
PoincareHelpers::Reduce(&m_memoizedExpressions[currentExpressionIndex], App::app()->localContext(), ExpressionNode::ReductionTarget::User);
m_memoizedExpressions[currentExpressionIndex] = m_memoizedExpressions[currentExpressionIndex].removeUnit(&newUnits);
double value = Shared::PoincareHelpers::ApproximateToScalar<double>(m_memoizedExpressions[currentExpressionIndex], App::app()->localContext());
ExpressionNode::ReductionContext reductionContext(
App::app()->localContext(),
Preferences::sharedPreferences()->complexFormat(),
Preferences::sharedPreferences()->angleUnit(),
ExpressionNode::ReductionTarget::User,
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined);
Unit::ChooseBestPrefixForValue(&newUnits, &value, reductionContext);
m_memoizedExpressions[currentExpressionIndex] = Multiplication::Builder(Number::FloatNumber(value), newUnits);
}
currentExpressionIndex++;
}
// 2. IS units only
assert(numberOfMemoizedExpressions < k_maxNumberOfCells - 1);
m_memoizedExpressions[numberOfMemoizedExpressions] = m_expression.clone();
Shared::PoincareHelpers::Simplify(&m_memoizedExpressions[numberOfMemoizedExpressions], App::app()->localContext(), ExpressionNode::ReductionTarget::User, Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, Poincare::ExpressionNode::UnitConversion::InternationalSystem);
numberOfMemoizedExpressions++;
// 3. Get rid of duplicates
Expression reduceExpression = m_expression.clone();
// Make m_expression compareable to m_memoizedExpressions (turn BasedInteger into Rational for instance)
Shared::PoincareHelpers::Simplify(&reduceExpression, App::app()->localContext(), ExpressionNode::ReductionTarget::User, Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, Poincare::ExpressionNode::UnitConversion::None);
currentExpressionIndex = 1;
while (currentExpressionIndex < numberOfMemoizedExpressions) {
bool duplicateFound = false;
for (size_t i = 0; i < currentExpressionIndex + 1; i++) {
// Compare the currentExpression to all previous memoized expressions and to m_expression
Expression comparedExpression = i == currentExpressionIndex ? reduceExpression : m_memoizedExpressions[i];
assert(!comparedExpression.isUninitialized());
if (comparedExpression.isIdenticalTo(m_memoizedExpressions[currentExpressionIndex])) {
numberOfMemoizedExpressions--;
// Shift next expressions
for (size_t j = currentExpressionIndex; j < numberOfMemoizedExpressions; j++) {
m_memoizedExpressions[j] = m_memoizedExpressions[j+1];
}
// Remove last expression
m_memoizedExpressions[numberOfMemoizedExpressions] = Expression();
// The current expression has been discarded, no need to increment the current index
duplicateFound = true;
break;
}
}
if (!duplicateFound) {
// The current expression is not a duplicate, check next expression
currentExpressionIndex++;
}
}
}
int UnitListController::numberOfRows() const {
int nbOfRows = 0;
for (size_t i = 0; i < k_maxNumberOfCells; i++) {
if (!m_memoizedExpressions[i].isUninitialized()) {
nbOfRows++;
}
}
return nbOfRows;
}
void UnitListController::computeLayoutAtIndex(int index) {
assert(!m_memoizedExpressions[index].isUninitialized());
m_layouts[index] = Shared::PoincareHelpers::CreateLayout(m_memoizedExpressions[index]);
}
I18n::Message UnitListController::messageAtIndex(int index) {
return (I18n::Message)0;
}
}

View File

@@ -0,0 +1,26 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_UNIT_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_UNIT_LIST_CONTROLLER_H
#include "expressions_list_controller.h"
namespace Calculation {
class UnitListController : public ExpressionsListController {
public:
UnitListController(EditExpressionController * editExpressionController) :
ExpressionsListController(editExpressionController) {}
void setExpression(Poincare::Expression e) override;
//ListViewDataSource
int numberOfRows() const override;
private:
void computeLayoutAtIndex(int index) override;
I18n::Message messageAtIndex(int index) override;
// Memoization of expressions
mutable Poincare::Expression m_memoizedExpressions[k_maxNumberOfCells];
};
}
#endif

View File

@@ -17,8 +17,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::CalculAppCapital;
}
int App::Descriptor::examinationLevel() {
return App::Descriptor::StrictExaminationLevel;
App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
return App::Descriptor::ExaminationLevel::Strict;
}
const Image * App::Descriptor::icon() {

View File

@@ -15,7 +15,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
int examinationLevel() override;
App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot {

View File

@@ -4,6 +4,6 @@ AdditionalResults = "Weitere Ergebnisse"
DecimalBase = "Dezimal"
HexadecimalBase = "Hexadezimal"
BinaryBase = "Binär"
PrimeFactors = "Primfaktor"
MixedFraction = "Gemischte Fraktion"
EuclideanDivision = "Euklidische Division"
PrimeFactors = "Primfaktoren"
MixedFraction = "Gemischte Zahl"
EuclideanDivision = "Division mit Rest"

View File

@@ -1,9 +1,9 @@
CalculApp = "Cálculo"
CalculAppCapital = "CÁLCULO"
AdditionalResults = "????"
DecimalBase = "????"
HexadecimalBase = "????"
BinaryBase = "????"
PrimeFactors = "????"
MixedFraction = "????"
EuclideanDivision = "????"
AdditionalResults = "Resultados adicionales"
DecimalBase = "Decimal"
HexadecimalBase = "Hexadecimal"
BinaryBase = "Binario"
PrimeFactors = "Factores primos"
MixedFraction = "Fracción mixta"
EuclideanDivision = "División euclidiana"

View File

@@ -0,0 +1,9 @@
CalculApp = "Calcolo"
CalculAppCapital = "CALCOLO"
AdditionalResults = "Risultati complementari"
DecimalBase = "Decimale"
HexadecimalBase = "Esadecimale"
BinaryBase = "Binario"
PrimeFactors = "Fattori primi"
MixedFraction = "Frazione mista"
EuclideanDivision = "Divisione euclidea"

View File

@@ -0,0 +1,9 @@
CalculApp = "Calculatie"
CalculAppCapital = "CALCULATIE"
AdditionalResults = "Bijkomende resultaten"
DecimalBase = "Decimaal"
HexadecimalBase = "Hexadecimaal"
BinaryBase = "Binaire"
PrimeFactors = "Priemfactoren"
MixedFraction = "Gemengde breuk"
EuclideanDivision = "Geheeltallige deling"

View File

@@ -1,9 +1,9 @@
CalculApp = "Cálculo"
CalculAppCapital = "CÁLCULO"
AdditionalResults = "????"
DecimalBase = "????"
HexadecimalBase = "????"
BinaryBase = "????"
PrimeFactors = "????"
MixedFraction = "????"
EuclideanDivision = "????"
AdditionalResults = "Resultados adicionais"
DecimalBase = "Decimal"
HexadecimalBase = "Hexadecimal"
BinaryBase = "Binário"
PrimeFactors = "Fatores primos"
MixedFraction = "Fração mista"
EuclideanDivision = "Divisão euclidiana"

View File

@@ -1,20 +1,22 @@
#include "calculation.h"
#include "../shared/poincare_helpers.h"
#include "../shared/scrollable_multiple_expressions_view.h"
#include "../global_preferences.h"
#include "../exam_mode_configuration.h"
#include "app.h"
#include <poincare/exception_checkpoint.h>
#include <poincare/undefined.h>
#include <poincare/unit.h>
#include <poincare/unreal.h>
#include <string.h>
#include <cmath>
#include <algorithm>
using namespace Poincare;
using namespace Shared;
namespace Calculation {
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
bool Calculation::operator==(const Calculation& c) {
return strcmp(inputText(), c.inputText()) == 0
&& strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), c.approximateOutputText(NumberOfSignificantDigits::Maximal)) == 0
@@ -39,8 +41,7 @@ Calculation * Calculation::next() const {
void Calculation::tidy() {
/* Reset height memoization (the complex format could have changed when
* re-entering Calculation app which would impact the heights). */
m_height = -1;
m_expandedHeight = -1;
resetHeightMemoization();
}
const char * Calculation::approximateOutputText(NumberOfSignificantDigits numberOfSignificantDigits) const {
@@ -125,123 +126,12 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul
}
}
KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpressionsInline) {
KDCoordinate result = expanded ? m_expandedHeight : m_height;
if (result >= 0) {
// Height already computed
return result;
}
// Get input height
Layout inputLayout = createInputLayout();
KDCoordinate inputHeight = inputLayout.layoutSize().height();
KDCoordinate inputWidth = inputLayout.layoutSize().width();
float singleMargin = 2 * Metric::CommonSmallMargin;
float doubleMargin = 4 * Metric::CommonSmallMargin;
bool singleLine = false;
KDCoordinate inputBaseline = inputLayout.baseline();
// Get exact output height if needed
Poincare::Layout exactLayout;
bool couldNotCreateExactLayout = false;
if (DisplaysExact(displayOutput(context))) {
// Create the exact output layout
exactLayout = createExactOutputLayout(&couldNotCreateExactLayout);
if (couldNotCreateExactLayout) {
if (displayOutput(context) != DisplayOutput::ExactOnly) {
forceDisplayOutput(DisplayOutput::ApproximateOnly);
} else {
/* We should only display the exact result, but we cannot create it
* -> raise an exception. */
ExceptionCheckpoint::Raise();
}
}
}
if (displayOutput(context) == DisplayOutput::ExactOnly) {
KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
KDCoordinate exactOutputWidth = exactLayout.layoutSize().width();
singleLine = exactOutputWidth + inputWidth < maxWidth - 40;
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact && !allExpressionsInline) {
KDCoordinate exactOutputBaseline = exactLayout.baseline();
result = maxCoordinate(inputBaseline, exactOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin;
} else {
if (allExpressionsInline) {
KDCoordinate exactOutputBaseline = exactLayout.baseline();
result = maxCoordinate(inputBaseline, exactOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline);
} else {
result = inputHeight + exactOutputHeight + doubleMargin;
}
}
void Calculation::setMemoizedHeight(bool expanded, KDCoordinate height) {
if (expanded) {
m_expandedHeight = height;
} else {
bool couldNotCreateApproximateLayout = false;
Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
if (displayOutput(context) == DisplayOutput::ApproximateOnly) {
Poincare::ExceptionCheckpoint::Raise();
} else {
/* Set the display output to ApproximateOnly, make room in the pool by
* erasing the exact layout, and retry to create the approximate layout */
forceDisplayOutput(DisplayOutput::ApproximateOnly);
exactLayout = Poincare::Layout();
couldNotCreateApproximateLayout = false;
approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
if (couldNotCreateApproximateLayout) {
Poincare::ExceptionCheckpoint::Raise();
}
}
}
KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height();
KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width();
singleLine = approximateOutputWidth + inputWidth < maxWidth - 40;
if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) {
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact && !allExpressionsInline) {
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
result = maxCoordinate(inputBaseline, approximateOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin;
} else {
if (allExpressionsInline) {
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
result = maxCoordinate(inputBaseline, approximateOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline);
} else {
result = inputHeight + approximateOutputHeight + doubleMargin;
}
}
} else {
assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded));
KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
KDCoordinate exactOutputBaseline = exactLayout.baseline();
KDCoordinate exactOutputWidth = exactLayout.layoutSize().width();
KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width();
singleLine = exactOutputWidth + approximateOutputWidth + inputWidth < maxWidth - 70;
KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) {
result = maxCoordinate(inputBaseline, maxCoordinate(exactOutputBaseline, approximateOutputBaseline)) + maxCoordinate(inputHeight - inputBaseline, maxCoordinate(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin;
} else {
if (allExpressionsInline) {
result = maxCoordinate(inputBaseline, maxCoordinate(exactOutputBaseline, approximateOutputBaseline)) + maxCoordinate(inputHeight - inputBaseline, maxCoordinate(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline));
} else {
KDCoordinate outputHeight = maxCoordinate(exactOutputBaseline, approximateOutputBaseline) + maxCoordinate(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline);
result = inputHeight + outputHeight + doubleMargin;
}
}
}
m_height = height;
}
/* For all display outputs except ExactAndApproximateToggle, the selected
* height and the usual height are identical. We update both heights in
* theses cases. */
if (displayOutput(context) != DisplayOutput::ExactAndApproximateToggle) {
m_height = result;
m_expandedHeight = result;
} else {
if (expanded) {
m_expandedHeight = result;
} else {
m_height = result;
}
}
return result;
}
Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
@@ -285,7 +175,7 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
ExpressionNode::Type::PredictionInterval
};
return e.isOfType(approximateOnlyTypes, sizeof(approximateOnlyTypes)/sizeof(ExpressionNode::Type));
}, context, true)
}, context)
)
{
m_displayOutput = DisplayOutput::ApproximateOnly;
@@ -302,15 +192,15 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
void Calculation::forceDisplayOutput(DisplayOutput d) {
m_displayOutput = d;
// Reset heights memoization as it might have changed when we modify the display output
m_height = -1;
m_expandedHeight = -1;
resetHeightMemoization();
}
bool Calculation::shouldOnlyDisplayExactOutput() {
/* If the input is a "store in a function", do not display the approximate
* result. This prevents x->f(x) from displaying x = undef. */
Expression i = input();
return i.type() == ExpressionNode::Type::Store
&& i.childAtIndex(1).type() == ExpressionNode::Type::Function;
return (i.type() == ExpressionNode::Type::Store && i.childAtIndex(1).type() == ExpressionNode::Type::Function)
|| strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), Undefined::Name()) == 0;
}
Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(Poincare::Context * context) {
@@ -340,6 +230,9 @@ Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(
}
Calculation::AdditionalInformationType Calculation::additionalInformationType(Context * context) {
if (ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::sharedGlobalPreferences()->examMode())) {
return AdditionalInformationType::None;
}
Preferences * preferences = Preferences::sharedPreferences();
Preferences::ComplexFormat complexFormat = Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), m_inputText);
Expression i = input();
@@ -361,8 +254,28 @@ Calculation::AdditionalInformationType Calculation::additionalInformationType(Co
if (input().isDefinedCosineOrSine(context, complexFormat, preferences->angleUnit()) || o.isDefinedCosineOrSine(context, complexFormat, preferences->angleUnit())) {
return AdditionalInformationType::Trigonometry;
}
// TODO: return AdditionalInformationType::Unit
if (o.hasUnit()) {
Expression unit;
PoincareHelpers::Reduce(&o, App::app()->localContext(), ExpressionNode::ReductionTarget::User,ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined, ExpressionNode::UnitConversion::None);
o = o.removeUnit(&unit);
if (Unit::IsSI(unit)) {
if (Unit::IsSISpeed(unit) || Unit::IsSIVolume(unit) || Unit::IsSIEnergy(unit)) {
/* All these units will provide misc. classic representatives in
* addition to the SI unit in additional information. */
return AdditionalInformationType::Unit;
}
if (Unit::IsSITime(unit)) {
/* If the number of seconds is above 60s, we can write it in the form
* of an addition: 23_min + 12_s for instance. */
double value = Shared::PoincareHelpers::ApproximateToScalar<double>(o, App::app()->localContext());
if (value > Unit::SecondsPerMinute) {
return AdditionalInformationType::Unit;
}
}
return AdditionalInformationType::None;
}
return AdditionalInformationType::Unit;
}
if (o.isBasedIntegerCappedBy(k_maximalIntegerWithAdditionalInformation)) {
return AdditionalInformationType::Integer;
}
@@ -376,4 +289,9 @@ Calculation::AdditionalInformationType Calculation::additionalInformationType(Co
return AdditionalInformationType::None;
}
void Calculation::resetHeightMemoization() {
m_height = -1;
m_expandedHeight = -1;
}
}

View File

@@ -84,7 +84,8 @@ public:
Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout);
// Memoization of height
KDCoordinate height(Poincare::Context * context, bool expanded = false, bool allExpressionsInline = false);
KDCoordinate memoizedHeight(bool expanded) { return expanded ? m_expandedHeight : m_height; }
void setMemoizedHeight(bool expanded, KDCoordinate height);
// Displayed output
DisplayOutput displayOutput(Poincare::Context * context);
@@ -99,6 +100,7 @@ private:
static constexpr int k_numberOfExpressions = 4;
static constexpr KDCoordinate k_heightComputationFailureHeight = 50;
static constexpr const char * k_maximalIntegerWithAdditionalInformation = "10000000000000000";
void resetHeightMemoization();
/* Buffers holding text expressions have to be longer than the text written
* by user (of maximum length TextField::maxBufferSize()) because when we
* print an expression we add omitted signs (multiplications, parenthesis...) */

View File

@@ -4,6 +4,7 @@
#include <poincare/rational.h>
#include <poincare/symbol.h>
#include <poincare/undefined.h>
#include "../exam_mode_configuration.h"
#include <assert.h>
using namespace Poincare;
@@ -98,8 +99,11 @@ ExpiringPointer<Calculation> CalculationStore::push(const char * text, Context *
// Outputs hold exact output, approximate output and its duplicate
constexpr static int numberOfOutputs = Calculation::k_numberOfExpressions - 1;
Expression outputs[numberOfOutputs] = {Expression(), Expression(), Expression()};
// SYMBOLIC COMPUTATION <= E12: PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, GlobalPreferences::sharedGlobalPreferences()->isInExamModeSymbolic()); // Symbolic computation
PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, GlobalPreferences::sharedGlobalPreferences()->isInExamModeSymbolic() ? Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition : Poincare::ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined);
if (ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::sharedGlobalPreferences()->examMode()) && outputs[1].hasUnit()) {
// Hide results with units on units if required by the exam mode configuration
outputs[1] = Undefined::Builder();
}
outputs[2] = outputs[1];
int numberOfSignificantDigits = Poincare::PrintFloat::k_numberOfStoredSignificantDigits;
for (int i = 0; i < numberOfOutputs; i++) {

View File

@@ -9,7 +9,7 @@ using namespace Poincare;
namespace Calculation {
EditExpressionController::ContentView::ContentView(Responder * parentResponder, TableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
EditExpressionController::ContentView::ContentView(Responder * parentResponder, CalculationSelectableTableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
View(),
m_mainView(subview),
m_expressionField(parentResponder, inputEventHandlerDelegate, textFieldDelegate, layoutFieldDelegate)
@@ -42,18 +42,18 @@ EditExpressionController::EditExpressionController(Responder * parentResponder,
ViewController(parentResponder),
m_historyController(historyController),
m_calculationStore(calculationStore),
m_contentView(this, (TableView *)m_historyController->view(), inputEventHandlerDelegate, this, this)
m_contentView(this, static_cast<CalculationSelectableTableView *>(m_historyController->view()), inputEventHandlerDelegate, this, this)
{
m_cacheBuffer[0] = 0;
}
void EditExpressionController::insertTextBody(const char * text) {
Container::activeApp()->setFirstResponder(this);
m_contentView.expressionField()->handleEventWithText(text, false, true);
}
void EditExpressionController::didBecomeFirstResponder() {
int lastRow = m_calculationStore->numberOfCalculations() > 0 ? m_calculationStore->numberOfCalculations()-1 : 0;
m_historyController->scrollToCell(0, lastRow);
m_contentView.mainView()->scrollToBottom();
m_contentView.expressionField()->setEditing(true, false);
Container::activeApp()->setFirstResponder(m_contentView.expressionField());
}

View File

@@ -8,6 +8,7 @@
#include "../shared/layout_field_delegate.h"
#include "history_controller.h"
#include "calculation_store.h"
#include "selectable_table_view.h"
namespace Calculation {
@@ -34,15 +35,15 @@ public:
private:
class ContentView : public View {
public:
ContentView(Responder * parentResponder, TableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate);
ContentView(Responder * parentResponder, CalculationSelectableTableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate);
void reload();
TableView * mainView() { return m_mainView; }
CalculationSelectableTableView * mainView() { return m_mainView; }
ExpressionField * expressionField() { return &m_expressionField; }
private:
int numberOfSubviews() const override { return 2; }
View * subviewAtIndex(int index) override;
void layoutSubviews(bool force = false) override;
TableView * m_mainView;
CalculationSelectableTableView * m_mainView;
ExpressionField m_expressionField;
};
void reloadView();

View File

@@ -7,7 +7,10 @@ namespace Calculation {
class ExpressionField : public ::ExpressionField {
public:
using ::ExpressionField::ExpressionField;
ExpressionField(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandler, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
::ExpressionField(parentResponder, inputEventHandler, textFieldDelegate, layoutFieldDelegate) {
setLayoutInsertionCursorEvent(Ion::Events::Up);
}
protected:
bool handleEvent(Ion::Events::Event event) override;
};

View File

@@ -1,5 +1,6 @@
#include "history_controller.h"
#include "app.h"
#include <poincare/exception_checkpoint.h>
#include <assert.h>
using namespace Shared;
@@ -15,7 +16,8 @@ HistoryController::HistoryController(EditExpressionController * editExpressionCo
m_complexController(editExpressionController),
m_integerController(editExpressionController),
m_rationalController(editExpressionController),
m_trigonometryController(editExpressionController)
m_trigonometryController(editExpressionController),
m_unitController(editExpressionController)
{
for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
m_calculationHistory[i].setParentResponder(&m_selectableTableView);
@@ -37,9 +39,9 @@ void HistoryController::reload() {
* the table view twice.
*/
if (numberOfRows() > 0) {
m_selectableTableView.scrollToCell(0, numberOfRows()-1);
m_selectableTableView.scrollToBottom();
// Force to reload last added cell (hide the burger and exact output if necessary)
tableViewDidChangeSelection(&m_selectableTableView, 0, numberOfRows()-1);
tableViewDidChangeSelectionAndDidScroll(&m_selectableTableView, 0, numberOfRows()-1);
}
}
@@ -78,11 +80,9 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
EditExpressionController * editController = (EditExpressionController *)parentResponder();
if (subviewType == SubviewType::Input) {
m_selectableTableView.deselectTable();
Container::activeApp()->setFirstResponder(editController);
editController->insertTextBody(calculationAtIndex(focusRow)->inputText());
} else if (subviewType == SubviewType::Output) {
m_selectableTableView.deselectTable();
Container::activeApp()->setFirstResponder(editController);
Shared::ExpiringPointer<Calculation> calculation = calculationAtIndex(focusRow);
ScrollableTwoExpressionsView::SubviewPosition outputSubviewPosition = selectedCell->outputView()->selectedSubviewPosition();
if (outputSubviewPosition == ScrollableTwoExpressionsView::SubviewPosition::Right
@@ -108,6 +108,8 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
vc = &m_integerController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Rational) {
vc = &m_rationalController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Unit) {
vc = &m_unitController;
}
if (vc) {
vc->setExpression(e);
@@ -120,24 +122,14 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
int focusRow = selectedRow();
SubviewType subviewType = selectedSubviewType();
m_selectableTableView.deselectTable();
EditExpressionController * editController = (EditExpressionController *)parentResponder();
m_calculationStore->deleteCalculationAtIndex(storeIndex(focusRow));
reload();
if (numberOfRows()== 0) {
Container::activeApp()->setFirstResponder(editController);
Container::activeApp()->setFirstResponder(parentResponder());
return true;
}
if (focusRow > 0) {
m_selectableTableView.selectCellAtLocation(0, focusRow-1);
} else {
m_selectableTableView.selectCellAtLocation(0, 0);
}
if (subviewType == SubviewType::Input) {
tableViewDidChangeSelection(&m_selectableTableView, 0, selectedRow());
} else {
tableViewDidChangeSelection(&m_selectableTableView, 0, -1);
}
m_selectableTableView.scrollToCell(0, selectedRow());
m_selectableTableView.selectCellAtLocation(0, focusRow > 0 ? focusRow - 1 : 0);
setSelectedSubviewType(subviewType, true, 0, selectedRow());
return true;
}
if (event == Ion::Events::Clear) {
@@ -148,9 +140,8 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
return true;
}
if (event == Ion::Events::Back) {
EditExpressionController * editController = (EditExpressionController *)parentResponder();
m_selectableTableView.deselectTable();
Container::activeApp()->setFirstResponder(editController);
Container::activeApp()->setFirstResponder(parentResponder());
return true;
}
return false;
@@ -160,19 +151,23 @@ Shared::ExpiringPointer<Calculation> HistoryController::calculationAtIndex(int i
return m_calculationStore->calculationAtIndex(storeIndex(i));
}
void HistoryController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
void HistoryController::tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
if (withinTemporarySelection || previousSelectedCellY == selectedRow()) {
return;
}
if (previousSelectedCellY == -1) {
setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY);
} else if (selectedRow() < previousSelectedCellY) {
setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY);
} else if (selectedRow() > previousSelectedCellY) {
setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY);
} else if (selectedRow() == -1) {
setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY);
} else {
HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell());
SubviewType nextSelectedSubviewType = selectedSubviewType();
if (selectedCell && !selectedCell->displaysSingleLine()) {
nextSelectedSubviewType = previousSelectedCellY < selectedRow() ? SubviewType::Input : SubviewType::Output;
}
setSelectedSubviewType(nextSelectedSubviewType, false, previousSelectedCellX, previousSelectedCellY);
}
// The selectedCell may change during setSelectedSubviewType
HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell());
if (selectedCell == nullptr) {
return;
@@ -208,22 +203,42 @@ KDCoordinate HistoryController::rowHeight(int j) {
return 0;
}
Shared::ExpiringPointer<Calculation> calculation = calculationAtIndex(j);
return calculation->height(App::app()->localContext(), j == selectedRow() && selectedSubviewType() == SubviewType::Output);
bool expanded = j == selectedRow() && selectedSubviewType() == SubviewType::Output;
KDCoordinate result = calculation->memoizedHeight(expanded);
if (result < 0) {
result = HistoryViewCell::Height(calculation.pointer(), expanded);
if (result < 0) {
// Raise, because Height modified the calculation and failed.
Poincare::ExceptionCheckpoint::Raise();
}
calculation->setMemoizedHeight(expanded, result);
}
/* We might want to put an assertion here to check the memoization:
* assert(result == HistoryViewCell::Height(calculation.pointer(), expanded));
* However, Height might fail due to pool memory exhaustion, in which case the
* assertion fails even if "result" had the right value. */
return result;
}
int HistoryController::typeAtLocation(int i, int j) {
return 0;
}
void HistoryController::scrollToCell(int i, int j) {
m_selectableTableView.scrollToCell(i, j);
}
bool HistoryController::calculationAtIndexToggles(int index) {
Context * context = App::app()->localContext();
return index >= 0 && index < m_calculationStore->numberOfCalculations() && calculationAtIndex(index)->displayOutput(context) == Calculation::DisplayOutput::ExactAndApproximateToggle;
}
void HistoryController::setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX, int previousSelectedY) {
// Avoid selecting non-displayed ellipsis
HistoryViewCell * selectedCell = static_cast<HistoryViewCell *>(m_selectableTableView.selectedCell());
if (subviewType == SubviewType::Ellipsis && selectedCell && selectedCell->additionalInformationType() == Calculation::AdditionalInformationType::None) {
subviewType = SubviewType::Output;
}
HistoryViewCellDataSource::setSelectedSubviewType(subviewType, sameCell, previousSelectedX, previousSelectedY);
}
void HistoryController::historyViewCellDidChangeSelection(HistoryViewCell ** cell, HistoryViewCell ** previousCell, int previousSelectedCellX, int previousSelectedCellY, SubviewType type, SubviewType previousType) {
/* If the selection change triggers the toggling of the outputs, we update
* the whole table as the height of the selected cell row might have changed. */

View File

@@ -9,6 +9,7 @@
#include "additional_outputs/integer_list_controller.h"
#include "additional_outputs/rational_list_controller.h"
#include "additional_outputs/trigonometry_list_controller.h"
#include "additional_outputs/unit_list_controller.h"
namespace Calculation {
@@ -30,8 +31,8 @@ public:
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
KDCoordinate rowHeight(int j) override;
int typeAtLocation(int i, int j) override;
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection = false) override;
void scrollToCell(int i, int j);
void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1) override;
void tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection = false) override;
private:
int storeIndex(int i) { return numberOfRows() - i - 1; }
Shared::ExpiringPointer<Calculation> calculationAtIndex(int i);
@@ -46,6 +47,7 @@ private:
IntegerListController m_integerController;
RationalListController m_rationalController;
TrigonometryListController m_trigonometryController;
UnitListController m_unitController;
};
}

View File

@@ -5,17 +5,12 @@
#include <poincare/exception_checkpoint.h>
#include <assert.h>
#include <string.h>
#include <algorithm>
namespace Calculation {
static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
/* HistoryViewCellDataSource */
HistoryViewCellDataSource::HistoryViewCellDataSource() :
m_selectedSubviewType(SubviewType::Output) {}
void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedCellX, int previousSelectedCellY) {
HistoryViewCell * selectedCell = nullptr;
HistoryViewCell * previouslySelectedCell = nullptr;
@@ -30,6 +25,7 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType,
if (selectedCell) {
selectedCell->reloadSubviewHighlight();
selectedCell->cellDidSelectSubview(subviewType, previousSubviewType);
Container::activeApp()->setFirstResponder(selectedCell);
}
if (previouslySelectedCell) {
previouslySelectedCell->cellDidSelectSubview(SubviewType::Input);
@@ -38,19 +34,35 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType,
/* HistoryViewCell */
HistoryViewCell::HistoryViewCell(Responder * parentResponder) :
Responder(parentResponder),
m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown),
m_calculationAdditionInformation(Calculation::AdditionalInformationType::None),
m_calculationExpanded(false),
m_inputView(this, Metric::CommonLargeMargin, Metric::CommonSmallMargin),
m_scrollableOutputView(this)
{
m_calculationCRC32 = 0;
KDCoordinate HistoryViewCell::Height(Calculation * calculation, bool expanded) {
HistoryViewCell cell(nullptr);
bool didForceOutput = false;
cell.setCalculation(calculation, expanded, &didForceOutput);
if (didForceOutput) {
/* We could not compute the height of the calculation as it is (the display
* output was forced to another value during the height computation).
* Warning: the display output of calculation was actually changed, so it
* will cause problems if we already did some computations with another
* display value. */
return -1;
}
KDRect ellipsisFrame = KDRectZero;
KDRect inputFrame = KDRectZero;
KDRect outputFrame = KDRectZero;
cell.computeSubviewFrames(Ion::Display::Width, KDCOORDINATE_MAX, &ellipsisFrame, &inputFrame, &outputFrame);
return k_margin + inputFrame.unionedWith(outputFrame).height() + k_margin;
}
Shared::ScrollableTwoExpressionsView * HistoryViewCell::outputView() {
return &m_scrollableOutputView;
HistoryViewCell::HistoryViewCell(Responder * parentResponder) :
Responder(parentResponder),
m_calculationCRC32(0),
m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown),
m_calculationAdditionInformation(Calculation::AdditionalInformationType::None),
m_inputView(this, k_inputViewHorizontalMargin, k_inputOutputViewsVerticalMargin),
m_scrollableOutputView(this),
m_calculationExpanded(false),
m_calculationSingleLine(false)
{
}
void HistoryViewCell::setEven(bool even) {
@@ -138,15 +150,6 @@ void HistoryViewCell::cellDidSelectSubview(HistoryViewCellDataSource::SubviewTyp
reloadScroll();
}
KDColor HistoryViewCell::backgroundColor() const {
KDColor background = m_even ? Palette::CalculationBackgroundEven : Palette::CalculationBackgroundOdd;
return background;
}
int HistoryViewCell::numberOfSubviews() const {
return 2 + displayedEllipsis();
}
View * HistoryViewCell::subviewAtIndex(int index) {
/* The order of the subviews should not matter here as they don't overlap.
* However, the order determines the order of redrawing as well. For several
@@ -170,29 +173,69 @@ View * HistoryViewCell::subviewAtIndex(int index) {
return views[index];
}
bool HistoryViewCell::ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth) {
// k_margin is the separation between the input and output.
return (inputViewWidth + k_margin + outputViewWidth) < Ion::Display::Width - Metric::EllipsisCellWidth;
}
void HistoryViewCell::layoutSubviews(bool force) {
KDCoordinate maxFrameWidth = bounds().width();
if (displayedEllipsis()) {
m_ellipsis.setFrame(KDRect(maxFrameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, bounds().height()), force);
maxFrameWidth -= Metric::EllipsisCellWidth;
} else {
m_ellipsis.setFrame(KDRectZero, force); // Required to mark previous rect as dirty
KDRect frameBounds = bounds();
if (bounds().width() <= 0 || bounds().height() <= 0) {
// TODO Make this behaviour in a non-virtual layoutSublviews, and all layout subviews should become privateLayoutSubviews
return;
}
KDRect ellipsisFrame = KDRectZero;
KDRect inputFrame = KDRectZero;
KDRect outputFrame = KDRectZero;
computeSubviewFrames(frameBounds.width(), frameBounds.height(), &ellipsisFrame, &inputFrame, &outputFrame);
m_ellipsis.setFrame(ellipsisFrame, force); // Required even if ellipsisFrame is KDRectZero, to mark previous rect as dirty
m_inputView.setFrame(inputFrame,force);
m_scrollableOutputView.setFrame(outputFrame, force);
}
void HistoryViewCell::computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame) {
assert(ellipsisFrame != nullptr && inputFrame != nullptr && outputFrame != nullptr);
if (displayedEllipsis()) {
*ellipsisFrame = KDRect(frameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, frameHeight);
frameWidth -= Metric::EllipsisCellWidth;
} else {
*ellipsisFrame = KDRectZero;
}
KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay();
m_inputView.setFrame(KDRect(
0, 0,
minCoordinate(maxFrameWidth, inputSize.width()),
inputSize.height()),
force);
KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay();
int singleLine = outputSize.width() + inputSize.width() < bounds().width() - 6;
int outputHeight = (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) ? (maxCoordinate(0, inputSize.height() - outputSize.height()) / 2) + maxCoordinate(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height();
m_scrollableOutputView.setFrame(KDRect(
maxCoordinate(0, maxFrameWidth - outputSize.width()),
outputHeight,
minCoordinate(maxFrameWidth, outputSize.width()),
outputSize.height()),
force);
/* To compute if the calculation is on a single line, use the expanded width
* if there is both an exact and an approximate layout. */
m_calculationSingleLine = ViewsCanBeSingleLine(inputSize.width(), m_scrollableOutputView.minimalSizeForOptimalDisplayFullSize().width());
KDCoordinate inputY = k_margin;
KDCoordinate outputY = k_margin;
if (m_calculationSingleLine && !m_inputView.layout().isUninitialized()) {
KDCoordinate inputBaseline = m_inputView.layout().baseline();
KDCoordinate outputBaseline = m_scrollableOutputView.baseline();
KDCoordinate baselineDifference = outputBaseline - inputBaseline;
if (baselineDifference > 0) {
inputY += baselineDifference;
} else {
outputY += -baselineDifference;
}
} else {
outputY += inputSize.height();
}
*inputFrame = KDRect(
0,
inputY,
std::min(frameWidth, inputSize.width()),
inputSize.height());
*outputFrame = KDRect(
std::max(0, frameWidth - outputSize.width()),
outputY,
std::min(frameWidth, outputSize.width()),
outputSize.height());
}
void HistoryViewCell::resetMemoization() {
@@ -203,7 +246,8 @@ void HistoryViewCell::resetMemoization() {
m_calculationCRC32 = 0;
}
void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded) {
void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded, bool * didForceOutput) {
assert(!didForceOutput || *didForceOutput == false);
uint32_t newCalculationCRC = Ion::crc32Byte((const uint8_t *)calculation, ((char *)calculation->next()) - ((char *) calculation));
if (newCalculationCRC == m_calculationCRC32 && m_calculationExpanded == expanded) {
return;
@@ -231,6 +275,9 @@ void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded) {
if (couldNotCreateExactLayout) {
if (calculation->displayOutput(context) != ::Calculation::Calculation::DisplayOutput::ExactOnly) {
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
if (didForceOutput) {
*didForceOutput = true;
}
} else {
/* We should only display the exact result, but we cannot create it
* -> raise an exception. */
@@ -253,6 +300,9 @@ void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded) {
/* Set the display output to ApproximateOnly, make room in the pool by
* erasing the exact layout, and retry to create the approximate layout */
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
if (didForceOutput) {
*didForceOutput = true;
}
exactOutputLayout = Poincare::Layout();
couldNotCreateApproximateLayout = false;
approximateOutputLayout = calculation->createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
@@ -286,31 +336,41 @@ void HistoryViewCell::didBecomeFirstResponder() {
}
bool HistoryViewCell::handleEvent(Ion::Events::Event event) {
assert(m_dataSource);
assert(m_dataSource != nullptr);
HistoryViewCellDataSource::SubviewType type = m_dataSource->selectedSubviewType();
if ((event == Ion::Events::Down && type == HistoryViewCellDataSource::SubviewType::Input) ||
(event == Ion::Events::Up && type == HistoryViewCellDataSource::SubviewType::Output) ||
(event == Ion::Events::Right && type != HistoryViewCellDataSource::SubviewType::Ellipsis && displayedEllipsis()) ||
(event == Ion::Events::Left && type == HistoryViewCellDataSource::SubviewType::Ellipsis)) {
HistoryViewCellDataSource::SubviewType otherSubviewType;
if (event == Ion::Events::Down) {
otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
} else if (event == Ion::Events::Up) {
otherSubviewType = HistoryViewCellDataSource::SubviewType::Input;
} else if (event == Ion::Events::Right) {
otherSubviewType = HistoryViewCellDataSource::SubviewType::Ellipsis;
} else {
assert(event == Ion::Events::Left);
otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
assert(type != HistoryViewCellDataSource::SubviewType::None);
HistoryViewCellDataSource::SubviewType otherSubviewType = HistoryViewCellDataSource::SubviewType::None;
if (m_calculationSingleLine) {
static_assert(
static_cast<int>(HistoryViewCellDataSource::SubviewType::None) == 0
&& static_cast<int>(HistoryViewCellDataSource::SubviewType::Input) == 1
&& static_cast<int>(HistoryViewCellDataSource::SubviewType::Output) == 2
&& static_cast<int>(HistoryViewCellDataSource::SubviewType::Ellipsis) == 3,
"The array types is not well-formed anymore");
HistoryViewCellDataSource::SubviewType types[] = {
HistoryViewCellDataSource::SubviewType::None,
HistoryViewCellDataSource::SubviewType::Input,
HistoryViewCellDataSource::SubviewType::Output,
displayedEllipsis() ? HistoryViewCellDataSource::SubviewType::Ellipsis : HistoryViewCellDataSource::SubviewType::None,
HistoryViewCellDataSource::SubviewType::None,
};
if (event == Ion::Events::Right || event == Ion::Events::Left) {
otherSubviewType = types[static_cast<int>(type) + (event == Ion::Events::Right ? 1 : -1)];
}
m_dataSource->setSelectedSubviewType(otherSubviewType, true);
return true;
} else if ((event == Ion::Events::Down && type == HistoryViewCellDataSource::SubviewType::Input)
|| (event == Ion::Events::Left && type == HistoryViewCellDataSource::SubviewType::Ellipsis))
{
otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
} else if (event == Ion::Events::Up && type == HistoryViewCellDataSource::SubviewType::Output) {
otherSubviewType = HistoryViewCellDataSource::SubviewType::Input;
} else if (event == Ion::Events::Right && type != HistoryViewCellDataSource::SubviewType::Ellipsis && displayedEllipsis()) {
otherSubviewType = HistoryViewCellDataSource::SubviewType::Ellipsis;
}
return false;
}
bool HistoryViewCell::displayedEllipsis() const {
return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None;
if (otherSubviewType == HistoryViewCellDataSource::SubviewType::None) {
return false;
}
m_dataSource->setSelectedSubviewType(otherSubviewType, true);
return true;
}
}

View File

@@ -12,14 +12,14 @@ class HistoryViewCell;
class HistoryViewCellDataSource {
public:
enum class SubviewType {
None,
Input,
Output,
Ellipsis
None = 0,
Input = 1,
Output = 2,
Ellipsis = 3
};
HistoryViewCellDataSource();
void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1);
SubviewType selectedSubviewType() { return m_selectedSubviewType; }
HistoryViewCellDataSource() : m_selectedSubviewType(SubviewType::Output) {}
virtual void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1);
SubviewType selectedSubviewType() const { return m_selectedSubviewType; }
private:
/* This method should belong to a delegate instead of a data source but as
* both the data source and the delegate will be the same controller, we
@@ -31,39 +31,52 @@ private:
class HistoryViewCell : public ::EvenOddCell, public Responder {
public:
constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin;
constexpr static KDCoordinate k_inputOutputViewsVerticalMargin = k_margin;
constexpr static KDCoordinate k_inputViewHorizontalMargin = Shared::AbstractScrollableMultipleExpressionsView::k_horizontalMargin;
static KDCoordinate Height(Calculation * calculation, bool expanded);
HistoryViewCell(Responder * parentResponder = nullptr);
static bool ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth);
void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None);
void setEven(bool even) override;
void setHighlighted(bool highlight) override;
void reloadSubviewHighlight();
void setDataSource(HistoryViewCellDataSource * dataSource) { m_dataSource = dataSource; }
bool displaysSingleLine() const {
return m_calculationSingleLine;
}
Responder * responder() override {
return this;
}
Poincare::Layout layout() const override;
KDColor backgroundColor() const override;
KDColor backgroundColor() const override { return m_even ? Palette::CalculationBackgroundEven : Palette::CalculationBackgroundOdd; }
void resetMemoization();
void setCalculation(Calculation * calculation, bool expanded);
int numberOfSubviews() const override;
void setCalculation(Calculation * calculation, bool expanded, bool * didForceOutput = nullptr);
int numberOfSubviews() const override { return 2 + displayedEllipsis(); }
View * subviewAtIndex(int index) override;
void layoutSubviews(bool force = false) override;
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
Shared::ScrollableTwoExpressionsView * outputView();
Shared::ScrollableTwoExpressionsView * outputView() { return &m_scrollableOutputView; }
ScrollableExpressionView * inputView() { return &m_inputView; }
Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; }
private:
constexpr static KDCoordinate k_resultWidth = 80;
void computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame);
void reloadScroll();
void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType);
bool displayedEllipsis() const;
bool displayedEllipsis() const {
return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None;
}
uint32_t m_calculationCRC32;
Calculation::DisplayOutput m_calculationDisplayOutput;
Calculation::AdditionalInformationType m_calculationAdditionInformation;
bool m_calculationExpanded;
ScrollableExpressionView m_inputView;
Shared::ScrollableTwoExpressionsView m_scrollableOutputView;
EvenOddCellWithEllipsis m_ellipsis;
HistoryViewCellDataSource * m_dataSource;
bool m_calculationExpanded;
bool m_calculationSingleLine;
};
}

View File

@@ -1,4 +1,5 @@
#include "selectable_table_view.h"
#include <algorithm>
namespace Calculation {
@@ -11,18 +12,23 @@ CalculationSelectableTableView::CalculationSelectableTableView(Responder * paren
setDecoratorType(ScrollView::Decorator::Type::None);
}
void CalculationSelectableTableView::scrollToBottom() {
KDCoordinate contentOffsetX = contentOffset().x();
KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(dataSource()->numberOfRows()) - maxContentHeightDisplayableWithoutScrolling();
setContentOffset(KDPoint(contentOffsetX, contentOffsetY));
}
void CalculationSelectableTableView::scrollToCell(int i, int j) {
::SelectableTableView::scrollToCell(i, j);
if (m_contentView.bounds().height() < bounds().height()) {
setTopMargin(bounds().height() - m_contentView.bounds().height());
} else {
setTopMargin(0);
}
::SelectableTableView::scrollToCell(i, j);
ScrollView::layoutSubviews();
if (m_contentView.bounds().height() - contentOffset().y() < bounds().height()) {
KDCoordinate contentOffsetX = contentOffset().x();
KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(dataSource()->numberOfRows()) - maxContentHeightDisplayableWithoutScrolling();
setContentOffset(KDPoint(contentOffsetX, contentOffsetY));
// Avoid empty space at the end of the table
scrollToBottom();
}
}
@@ -31,23 +37,63 @@ void CalculationSelectableTableView::scrollToSubviewOfTypeOfCellAtLocation(Histo
return;
}
/* As we scroll, the selected calculation does not use the same history view
* cell, thus, we want to deselect the previous used history view cell. */
* cell, thus, we want to deselect the previous used history view cell. (*) */
unhighlightSelectedCell();
/* Main part of the scroll */
HistoryViewCell * cell = static_cast<HistoryViewCell *>(selectedCell());
assert(cell);
KDCoordinate contentOffsetX = contentOffset().x();
KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(j+1) - maxContentHeightDisplayableWithoutScrolling();
if (subviewType == HistoryViewCellDataSource::SubviewType::Input) {
if (j == 0) {
contentOffsetY = 0;
} else {
contentOffsetY = dataSource()->cumulatedHeightFromIndex(j);
}
KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(j);
if (cell->displaysSingleLine() && dataSource()->rowHeight(j) > maxContentHeightDisplayableWithoutScrolling()) {
/* If we cannot display the full calculation, we display the selected
* layout as close as possible to the top of the screen without drawing
* empty space between the history and the input field.
*
* Below are some values we can assign to contentOffsetY, and the kinds of
* display they entail :
* (the selected cell is at index j)
*
* 1 - cumulatedHeightFromIndex(j)
* Aligns the top of the cell with the top of the zone in which the
* history can be drawn.
*
* 2 - (cumulatedHeightFromIndex(j+1)
* - maxContentHeightDisplayableWithoutScrolling())
* Aligns the bottom of the cell with the top of the input field.
*
* 3 - cumulatedHeightFromIndex(j) + baseline1 - baseline2
* Aligns the top of the selected layout with the top of the screen (only
* used when the selected layout is the smallest).
*
* The following drawing shows where the calculation would be aligned with
* each value of contentOffsetY, for the calculation (1/3)/(4/2) = 1/6.
*
* (1) (2) (3)
* +--------------+ +--------------+ +--------------+
* | 1 | | --- - | | 3 1 |
* | - | | 4 6 | | --- - |
* | 3 1 | | - | | 4 6 |
* | --- - | | 2 | | - |
* +--------------+ +--------------+ +--------------+
* | (1/3)/(4/2) | | (1/3)/(4/2) | | (1/3)/(4/2) |
* +--------------+ +--------------+ +--------------+
*
* */
contentOffsetY += std::min(
dataSource()->rowHeight(j) - maxContentHeightDisplayableWithoutScrolling(),
std::max(0, (cell->inputView()->layout().baseline() - cell->outputView()->baseline()) * (subviewType == HistoryViewCellDataSource::SubviewType::Input ? -1 : 1)));
} else if (subviewType != HistoryViewCellDataSource::SubviewType::Input) {
contentOffsetY += dataSource()->rowHeight(j) - maxContentHeightDisplayableWithoutScrolling();
}
setContentOffset(KDPoint(contentOffsetX, contentOffsetY));
/* For the same reason, we have to rehighlight the new history view cell and
* reselect the first responder. */
HistoryViewCell * cell = (HistoryViewCell *)(selectedCell());
/* For the same reason as (*), we have to rehighlight the new history view
* cell and reselect the first responder.
* We have to recall "selectedCell" because when the table might have been
* relayouted in "setContentOffset".*/
cell = static_cast<HistoryViewCell *>(selectedCell());
assert(cell);
cell->setHighlighted(true);
Container::activeApp()->setFirstResponder(cell);

View File

@@ -9,6 +9,7 @@ class CalculationSelectableTableView : public ::SelectableTableView {
public:
CalculationSelectableTableView(Responder * parentResponder, TableViewDataSource * dataSource,
SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate = nullptr);
void scrollToBottom();
void scrollToCell(int i, int j) override;
void scrollToSubviewOfTypeOfCellAtLocation(HistoryViewCellDataSource::SubviewType subviewType, int i, int j);
};

View File

@@ -11,42 +11,31 @@ app_code_src = $(addprefix apps/code/,\
editor_view.cpp \
helpers.cpp \
menu_controller.cpp \
python_toolbox.cpp \
python_text_area.cpp \
sandbox_controller.cpp \
script.cpp \
script_name_cell.cpp \
script_node_cell.cpp \
script_parameter_controller.cpp \
)
app_code_test_src = $(addprefix apps/code/,\
python_toolbox.cpp \
script.cpp \
script_node_cell.cpp \
script_store.cpp \
script_template.cpp \
variable_box_empty_controller.cpp \
variable_box_controller.cpp \
)
app_src += $(app_code_src)
i18n_files += $(addprefix apps/code/,\
base.de.i18n\
base.en.i18n\
base.es.i18n\
base.fr.i18n\
base.pt.i18n\
base.hu.i18n\
base.universal.i18n\
catalog.de.i18n\
catalog.en.i18n\
catalog.es.i18n\
catalog.fr.i18n\
catalog.pt.i18n\
catalog.hu.i18n\
catalog.universal.i18n\
toolbox.de.i18n\
toolbox.en.i18n\
toolbox.es.i18n\
toolbox.fr.i18n\
toolbox.pt.i18n\
toolbox.hu.i18n\
toolbox.universal.i18n\
tests_src += $(addprefix apps/code/test/,\
variable_box_controller.cpp\
)
app_code_src += $(app_code_test_src)
apps_src += $(app_code_src)
i18n_files += $(call i18n_with_universal_for,code/base)
i18n_files += $(call i18n_with_universal_for,code/catalog)
i18n_files += $(call i18n_with_universal_for,code/toolbox)
$(eval $(call depends_on_image,apps/code/app.cpp,apps/code/code_icon.png))

View File

@@ -14,8 +14,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::CodeAppCapital;
}
int App::Descriptor::examinationLevel() {
return App::Descriptor::BasicExaminationLevel;
App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
return App::Descriptor::ExaminationLevel::Basic;
}
const Image * App::Descriptor::icon() {
@@ -67,7 +67,7 @@ void App::Snapshot::setOpt(const char * name, const char * value) {
const char * scriptContent = separator;
Code::ScriptTemplate script(scriptName, scriptContent);
m_scriptStore.addScriptFromTemplate(&script);
m_scriptStore.scriptNamed(scriptName).toggleImportationStatus(); // set Importation Status to 1
ScriptStore::ScriptNamed(scriptName).toggleAutoimportationStatus(); // set Importation Status to 1
return;
}
if (strcmp(name, "lock-on-console") == 0) {

View File

@@ -18,7 +18,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
int examinationLevel() override;
App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot {
@@ -51,6 +51,7 @@ public:
}
StackViewController * stackViewController() { return &m_codeStackViewController; }
ConsoleController * consoleController() { return &m_consoleController; }
MenuController * menuController() { return &m_menuController; }
/* Responder */
bool handleEvent(Ion::Events::Event event) override;

View File

@@ -1,9 +1,15 @@
Console = "Interaktive Konsole"
AddScript = "Skript hinzufügen"
ScriptOptions = "Skriptoptionen"
ExecuteScript = "Skript ausführen"
AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _"
Autocomplete = "Autovervollständigung"
AutoImportScript = "Automatischer Import in Konsole"
BuiltinsAndKeywords = "Native Funktionen und Schlüsselwörter"
Console = "Interaktive Konsole"
DeleteScript = "Skript löschen"
DuplicateScript = "Skript duplizieren"
ExecuteScript = "Skript ausführen"
FunctionsAndVariables = "Funktionen und Variablen"
AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _"
ImportedModulesAndScripts = "Importierte Module und Skripte"
NoWordAvailableHere = "Kein Wort ist hier verfübar."
ScriptInProgress = "Aktuelle Skript"
ScriptOptions = "Skriptoptionen"
ScriptSize = "Script size"

View File

@@ -1,9 +1,15 @@
Console = "Python shell"
AddScript = "Add a script"
ScriptOptions = "Script options"
ExecuteScript = "Execute script"
AllowedCharactersaz09 = "Allowed characters: a-z, 0-9, _"
Autocomplete = "Autocomplete"
AutoImportScript = "Auto import in shell"
BuiltinsAndKeywords = "Builtins and keywords"
Console = "Python shell"
DeleteScript = "Delete script"
DuplicateScript = "Duplicate script"
ExecuteScript = "Execute script"
FunctionsAndVariables = "Functions and variables"
AllowedCharactersaz09 = "Allowed characters: a-z, 0-9, _"
ImportedModulesAndScripts = "Imported modules and scripts"
NoWordAvailableHere = "No word available here."
ScriptInProgress = "Script in progress"
ScriptOptions = "Script options"
ScriptSize = "Script size"

View File

@@ -1,9 +1,15 @@
Console = "Interprete de comandos"
AddScript = "Agregar un archivo"
ScriptOptions = "Opciones del archivo"
ExecuteScript = "Ejecutar el archivo"
AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"
Autocomplete = "Autocompleción"
AutoImportScript = "Importación auto en intérprete"
BuiltinsAndKeywords = "Funciones nativas y palabras clave"
Console = "Interprete de comandos"
DeleteScript = "Eliminar el archivo"
DuplicateScript = "Duplicar el guión"
ExecuteScript = "Ejecutar el archivo"
FunctionsAndVariables = "Funciones y variables"
AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"
ImportedModulesAndScripts = "Módulos y archivos importados"
NoWordAvailableHere = "No hay ninguna palabra disponible aquí."
ScriptInProgress = "Archivo en curso"
ScriptOptions = "Opciones del archivo"
ScriptSize = "Script size"

View File

@@ -1,9 +1,15 @@
Console = "Console d'exécution"
AddScript = "Ajouter un script"
ScriptOptions = "Options de script"
ExecuteScript = "Exécuter le script"
AllowedCharactersaz09 = "Caractères autorisés : a-z, 0-9, _"
Autocomplete = "Auto-complétion"
AutoImportScript = "Importation auto dans la console"
BuiltinsAndKeywords = "Fonctions natives et mots-clés"
Console = "Console d'exécution"
DeleteScript = "Supprimer le script"
DuplicateScript = "Dupliquer le script"
ExecuteScript = "Exécuter le script"
FunctionsAndVariables = "Fonctions et variables"
AllowedCharactersaz09 = "Caractères autorisés : a-z, 0-9, _"
ImportedModulesAndScripts = "Modules et scripts importés"
NoWordAvailableHere = "Aucun mot disponible à cet endroit."
ScriptInProgress = "Script en cours"
ScriptOptions = "Options de script"
ScriptSize = "Script size"

View File

@@ -1,9 +1,15 @@
Console = "Konzol"
AddScript = "Script hozzadáadása"
ScriptOptions = "Script beállítások"
ExecuteScript = "Script indítása"
AllowedCharactersaz09 = "Engedélyezett karakterek: a-z, 0-9, _"
Autocomplete = "Autocomplete"
AutoImportScript = "Script automata importálása"
BuiltinsAndKeywords = "Builtins and keywords"
Console = "Konzol"
DeleteScript = "Script törlése"
DuplicateScript = "Script másolása"
ExecuteScript = "Script indítása"
FunctionsAndVariables = "Függvények és változók"
AllowedCharactersaz09 = "Engedélyezett karakterek: a-z, 0-9, _"
ImportedModulesAndScripts = "Imported modules and scripts"
NoWordAvailableHere = "No word available here."
ScriptInProgress = "Script in progress"
ScriptOptions = "Script beállítások"
ScriptSize = "Script size"

15
apps/code/base.it.i18n Normal file
View File

@@ -0,0 +1,15 @@
AddScript = "Aggiungere script"
AllowedCharactersaz09 = "Caratteri consentiti : a-z, 0-9, _"
Autocomplete = "Autocompletamento"
AutoImportScript = "Importazione automatica dello script"
BuiltinsAndKeywords = "Funzioni native e parole chiave"
Console = "Console d'esecuzione"
DeleteScript = "Eliminare lo script"
DuplicateScript = "Duplicate script"
ExecuteScript = "Eseguire lo script"
FunctionsAndVariables = "Funzioni e variabili"
ImportedModulesAndScripts = "Moduli e scripts importati"
NoWordAvailableHere = "Nessuna parola disponibile qui."
ScriptInProgress = "Script in corso"
ScriptOptions = "Opzioni dello script"
ScriptSize = "Script Size"

15
apps/code/base.nl.i18n Normal file
View File

@@ -0,0 +1,15 @@
AddScript = "Script toevoegen"
AllowedCharactersaz09 = "Toegestane tekens: a-z, 0-9, _"
Autocomplete = "Autocomplete"
AutoImportScript = "Automatisch importeren in shell"
BuiltinsAndKeywords = "Builtins and keywords"
Console = "Python shell"
DeleteScript = "Script verwijderen"
DuplicateScript = "Duplicate script"
ExecuteScript = "Script uitvoeren"
FunctionsAndVariables = "Functies en variabelen"
ImportedModulesAndScripts = "Imported modules and scripts"
NoWordAvailableHere = "No word available here."
ScriptInProgress = "Script in progress"
ScriptOptions = "Script opties"
ScriptSize = "Script Size"

View File

@@ -1,9 +1,15 @@
Console = "Interpretador interativo"
AddScript = "Adicionar um script"
ScriptOptions = "Opções de script"
ExecuteScript = "Executar o script"
AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"
Autocomplete = "Preenchimento automático"
AutoImportScript = "Importação auto no interpretador"
BuiltinsAndKeywords = "Funções nativas e palavras-chave"
Console = "Interpretador interativo"
DeleteScript = "Eliminar o script"
DuplicateScript = "Duplicar o script"
ExecuteScript = "Executar o script"
FunctionsAndVariables = "Funções e variáveis"
AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"
ImportedModulesAndScripts = "Módulos e scripts importados"
NoWordAvailableHere = "Nenhuma palavra disponível aqui."
ScriptInProgress = "Script em curso"
ScriptOptions = "Opções de script"
ScriptSize = "Script Size"

View File

@@ -26,7 +26,18 @@ PythonCeil = "Aufrundung"
PythonChoice = "Zufallszahl aus der Liste"
PythonClear = "Leere die Liste"
PythonCmathFunction = "cmath-Modul-Funktionspräfix"
PythonColor = "Definiert eine RGB-Farbe"
PythonColor = "Definiere eine RGB-Farbe"
PythonColorBlack = "Black color"
PythonColorBlue = "Blue color"
PythonColorBrown = "Brown color"
PythonColorGreen = "Green color"
PythonColorGrey = "Grey color"
PythonColorOrange = "Orange color"
PythonColorPink = "Pink color"
PythonColorPurple = "Purple color"
PythonColorRed = "Red color"
PythonColorWhite = "White color"
PythonColorYellow = "Yellow color"
PythonComplex = "a+ib zurückgeben"
PythonCopySign = "x mit dem Vorzeichen von y"
PythonCos = "Kosinus"
@@ -45,10 +56,10 @@ PythonFillRect = "Malt ein Rechteck bei Pixel (x,y)"
PythonFloat = "Wandelt x zu float um"
PythonFloor = "Floor"
PythonFmod = "a modulo b"
PythonFrExp = "Rest und Exponent von x"
PythonGamma = "Gammafunktion"
PythonGetPixel = "Farbe von Pixel (x,y)"
PythonGetrandbits = "Ganzzahl mit k zufälligen Bits"
PythonFrExp = "Mantissa and exponent of x: (m,e)"
PythonGamma = "Gamma function"
PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
PythonGrid = "Toggle the visibility of the grid"
PythonHex = "Ganzzahl zu Hexadecimal"
PythonHist = "Draw the histogram of x"
@@ -139,10 +150,10 @@ PythonRadians = "Convert x from degrees to radians"
PythonRandint = "Random integer in [a,b]"
PythonRandom = "Floating point number in [0,1["
PythonRandomFunction = "random module function prefix"
PythonRandrange = "Random number in range(start, stop)"
PythonRandrange = "Random number in range(start,stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
PythonRect = "z in cartesian coordinates"
PythonRect = "Convert to cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
@@ -162,39 +173,45 @@ PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Schwarze Farbe"
PythonTurtleBlue = "Blaue Farbe"
PythonTurtleBrown = "Braune Farbe"
PythonTurtleCircle = "Circle of radius r pixels"
PythonTurtleColor = "Stiftfarbe setzen"
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
PythonTurtleForward = "Move forward by x pixels"
PythonTurtleFunction = "turtle module function prefix"
PythonTurtleGoto = "Move to (x,y) coordinates"
PythonTurtleGreen = "Grüne Farbe"
PythonTurtleGrey = "Graue Farbe"
PythonTurtleHeading = "Return the current heading"
PythonTurtleHideturtle = "Hide the turtle"
PythonTurtleIsdown = "Return True if the pen is down"
PythonTurtleLeft = "Turn left by a degrees"
PythonTurtleOrange = "Orange color"
PythonTurtlePendown = "Pull the pen down"
PythonTurtlePensize = "Set the line thickness to x pixels"
PythonTurtlePenup = "Pull the pen up"
PythonTurtlePink = "Pinke Farbe"
PythonTurtlePosition = "Return the current (x,y) location"
PythonTurtlePurple = "Purple color"
PythonTurtleRed = "Rote Farbe"
PythonTurtleReset = "Reset the drawing"
PythonTurtleRight = "Turn right by a degrees"
PythonTurtleSetheading = "Set the orientation to a degrees"
PythonTurtleSetposition = "Position des turtles"
PythonTurtleShowturtle = "Die turtle anzeigen"
PythonTurtleSpeed = "Zeichengeschwindigkeit zwischen 0 und 10"
PythonTurtleWhite = "Weiße Farbe"
PythonTurtleYellow = "Gelbe Farbe"
PythonUniform = "Fließkommazahl in [a,b]"
PythonTimeFromImport = "Import time module"
PythonTimeImport = "Import time module"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Show the turtle"
PythonTurtleSpeed = "Drawing speed between 0 and 10"
PythonTurtleWrite = "Display a text"
PythonUniform = "Floating point number in [a,b]"
PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
PythonTimeSleep = "Warten Sie n Sekunden lang"
PythonTimeMonotonic = "Monotone Zeit zurückgeben"
PythonTimeSleep = "Wait for n second"
PythonMonotonic = "Return monotonic time"
PythonFileOpen = "Öffnet eine Datei"
PythonFileSeekable = "Ist eine Datei durchsuchbar?"
PythonFileSeek = "Dateicursor verschieben"
PythonFileTell = "Cursorposition der Datei abrufen"
PythonFileClose = "Schließt eine Datei"
PythonFileClosed = "Wenn Datei geschlossen wurde"
PythonFileRead = "Bis zu size Bytes lesen"
PythonFileWrite = "Schreibe b in die Datei"
PythonFileReadline = "Lies eine Zeile"
PythonFileReadlines = "Liest eine Liste von Zeilen"
PythonFileTruncate = "Größe der Datei ändern"
PythonFileWritelines = "Schreibt eine Liste von Zeilen"
PythonFileName = "Dateiname"
PythonFileMode = "Dateiöffnungsmodus"
PythonFileReadable = "Ist die Datei lesbar?"
PythonFileWritable = "Ist die Datei beschreibbar?"

View File

@@ -1,7 +1,7 @@
PythonPound = "Comment"
PythonPercent = "Modulo"
Python1J = "Imaginary i"
PythonLF = "Line feed"
PythonLF = "line feed"
PythonTab = "Tabulation"
PythonAmpersand = "Bitwise and"
PythonSymbolExp = "Bitwise exclusive or"
@@ -27,6 +27,17 @@ PythonChoice = "Random number in the list"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath module function prefix"
PythonColor = "Define a rgb color"
PythonColorBlack = "Black color"
PythonColorBlue = "Blue color"
PythonColorBrown = "Brown color"
PythonColorGreen = "Green color"
PythonColorGrey = "Grey color"
PythonColorOrange = "Orange color"
PythonColorPink = "Pink color"
PythonColorPurple = "Purple color"
PythonColorRed = "Red color"
PythonColorWhite = "White color"
PythonColorYellow = "Yellow color"
PythonComplex = "Return a+ib"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Cosine"
@@ -45,7 +56,7 @@ PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonFloor = "Floor"
PythonFmod = "a modulo b"
PythonFrExp = "Mantissa and exponent of x"
PythonFrExp = "Mantissa and exponent of x: (m,e)"
PythonGamma = "Gamma function"
PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
@@ -139,10 +150,10 @@ PythonRadians = "Convert x from degrees to radians"
PythonRandint = "Random integer in [a,b]"
PythonRandom = "Floating point number in [0,1["
PythonRandomFunction = "random module function prefix"
PythonRandrange = "Random number in range(start, stop)"
PythonRandrange = "Random number in range(start,stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
PythonRect = "z in cartesian coordinates"
PythonRect = "Convert to cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
@@ -162,39 +173,45 @@ PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Black color"
PythonTurtleBlue = "Blue color"
PythonTurtleBrown = "Brown color"
PythonTurtleCircle = "Circle of radius r pixels"
PythonTurtleColor = "Set the pen color"
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
PythonTurtleForward = "Move forward by x pixels"
PythonTurtleFunction = "turtle module function prefix"
PythonTurtleGoto = "Move to (x,y) coordinates"
PythonTurtleGreen = "Green color"
PythonTurtleGrey = "Grey color"
PythonTurtleHeading = "Return the current heading"
PythonTurtleHideturtle = "Hide the turtle"
PythonTurtleIsdown = "Return True if the pen is down"
PythonTurtleLeft = "Turn left by a degrees"
PythonTurtleOrange = "Orange color"
PythonTurtlePendown = "Pull the pen down"
PythonTurtlePensize = "Set the line thickness to x pixels"
PythonTurtlePenup = "Pull the pen up"
PythonTurtlePink = "Pink color"
PythonTurtlePosition = "Return the current (x,y) location"
PythonTurtlePurple = "Purple color"
PythonTurtleRed = "Red color"
PythonTurtleReset = "Reset the drawing"
PythonTurtleRight = "Turn right by a degrees"
PythonTurtleSetheading = "Set the orientation to a degrees"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Show the turtle"
PythonTurtleSpeed = "Drawing speed between 0 and 10"
PythonTurtleWhite = "White color"
PythonTurtleYellow = "Yellow color"
PythonTurtleWrite = "Display a text"
PythonUniform = "Floating point number in [a,b]"
PythonTimeFromImport = "Import time module"
PythonTimeImport = "Import time module"
PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
PythonTimeSleep = "Wait for n second"
PythonTimeMonotonic = "Return monotonic time"
PythonMonotonic = "Return monotonic time"
PythonFileOpen = "Opens a file"
PythonFileSeekable = "Tells if seek can be used on a file"
PythonFileSeek = "Move file's cursor"
PythonFileTell = "Get file's cursor location"
PythonFileClose = "Closes a file"
PythonFileClosed = "True if file was closed"
PythonFileRead = "Read up to size bytes"
PythonFileWrite = "Write b into file"
PythonFileReadline = "Reads a line or up to size bytes"
PythonFileReadlines = "Reads a list of lines"
PythonFileTruncate = "Resize the file to size"
PythonFileWritelines = "Writes a list of lines"
PythonFileName = "Contains file's name"
PythonFileMode = "Contains file's open mode"
PythonFileReadable = "Tells if read can be used on a file"
PythonFileWritable = "Tells if write can be used on a file"

View File

@@ -27,6 +27,17 @@ PythonChoice = "Random number in the list"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath module function prefix"
PythonColor = "Define a rgb color"
PythonColorBlack = "Black color"
PythonColorBlue = "Blue color"
PythonColorBrown = "Brown color"
PythonColorGreen = "Green color"
PythonColorGrey = "Grey color"
PythonColorOrange = "Orange color"
PythonColorPink = "Pink color"
PythonColorPurple = "Purple color"
PythonColorRed = "Red color"
PythonColorWhite = "White color"
PythonColorYellow = "Yellow color"
PythonComplex = "Return a+ib"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Cosine"
@@ -45,7 +56,7 @@ PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonFloor = "Floor"
PythonFmod = "a modulo b"
PythonFrExp = "Mantissa and exponent of x"
PythonFrExp = "Mantissa and exponent of x: (m,e)"
PythonGamma = "Gamma function"
PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
@@ -139,10 +150,10 @@ PythonRadians = "Convert x from degrees to radians"
PythonRandint = "Random integer in [a,b]"
PythonRandom = "Floating point number in [0,1["
PythonRandomFunction = "random module function prefix"
PythonRandrange = "Random number in range(start, stop)"
PythonRandrange = "Random number in range(start,stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
PythonRect = "z in cartesian coordinates"
PythonRect = "Convert to cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
@@ -162,39 +173,45 @@ PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Black color"
PythonTurtleBlue = "Blue color"
PythonTurtleBrown = "Brown color"
PythonTurtleCircle = "Circle of radius r pixels"
PythonTurtleColor = "Set the pen color"
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
PythonTurtleForward = "Move forward by x pixels"
PythonTurtleFunction = "turtle module function prefix"
PythonTurtleGoto = "Move to (x,y) coordinates"
PythonTurtleGreen = "Green color"
PythonTurtleGrey = "Grey color"
PythonTurtleHeading = "Return the current heading"
PythonTurtleHideturtle = "Hide the turtle"
PythonTurtleIsdown = "Return True if the pen is down"
PythonTurtleLeft = "Turn left by a degrees"
PythonTurtleOrange = "Orange color"
PythonTurtlePendown = "Pull the pen down"
PythonTurtlePensize = "Set the line thickness to x pixels"
PythonTurtlePenup = "Pull the pen up"
PythonTurtlePink = "Pink color"
PythonTurtlePosition = "Return the current (x,y) location"
PythonTurtlePurple = "Purple color"
PythonTurtleRed = "Red color"
PythonTurtleReset = "Reset the drawing"
PythonTurtleRight = "Turn right by a degrees"
PythonTurtleSetheading = "Set the orientation to a degrees"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Show the turtle"
PythonTurtleSpeed = "Drawing speed between 0 and 10"
PythonTurtleWhite = "White color"
PythonTurtleYellow = "Yellow color"
PythonTurtleWrite = "Display a text"
PythonUniform = "Floating point number in [a,b]"
PythonTimeFromImport = "Import time module"
PythonTimeImport = "Import time module"
PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
PythonTimeSleep = "Esperar n segundos"
PythonTimeMonotonic = "Tiempo monótono de retorno"
PythonMonotonic = "Tiempo monótono de retorno"
PythonFileOpen = "Opens a file"
PythonFileSeekable = "Tells if seek can be used on a file"
PythonFileSeek = "Move file's internal cursor"
PythonFileTell = "Get file's internal cursor location"
PythonFileClose = "Closes a file"
PythonFileClosed = "True if file was closed"
PythonFileRead = "Read up to size bytes"
PythonFileWrite = "Write b into file"
PythonFileReadline = "Reads a line or up to size bytes"
PythonFileReadlines = "Reads a list of lines"
PythonFileTruncate = "Resize the file to size"
PythonFileWritelines = "Writes a list of lines"
PythonFileName = "Contains file's name"
PythonFileMode = "Contains file's open mode"
PythonFileReadable = "Tells if read can be used on a file"
PythonFileWritable = "Tells if write can be used on a file"

View File

@@ -27,6 +27,17 @@ PythonChoice = "Nombre aléatoire dans la liste"
PythonClear = "Vide la liste"
PythonCmathFunction = "Préfixe fonction du module cmath"
PythonColor = "Définit une couleur rvb"
PythonColorBlack = "Couleur noire"
PythonColorBlue = "Couleur bleue"
PythonColorBrown = "Couleur marron"
PythonColorGreen = "Couleur verte"
PythonColorGrey = "Couleur grise"
PythonColorOrange = "Couleur orange"
PythonColorPink = "Couleur rose"
PythonColorPurple = "Couleur violette"
PythonColorRed = "Couleur rouge"
PythonColorWhite = "Couleur blanche"
PythonColorYellow = "Couleur jaune"
PythonComplex = "Renvoie a+ib"
PythonCopySign = "Renvoie x avec le signe de y"
PythonCos = "Cosinus"
@@ -139,7 +150,7 @@ PythonRadians = "Conversion de degrés en radians"
PythonRandint = "Entier aléatoire dans [a,b]"
PythonRandom = "Nombre décimal dans [0,1["
PythonRandomFunction = "Préfixe fonction du module random"
PythonRandrange = "Nombre dans range(start, stop)"
PythonRandrange = "Nombre dans range(start,stop)"
PythonRangeStartStop = "Liste de start à stop-1"
PythonRangeStop = "Liste de 0 à stop-1"
PythonRect = "Conversion en algébrique"
@@ -162,39 +173,45 @@ PythonText = "Affiche un texte en (x,y)"
PythonTimeFunction = "Préfixe fonction module time"
PythonTrunc = "Troncature entière"
PythonTurtleBackward = "Recule de x pixels"
PythonTurtleBlack = "Couleur noire"
PythonTurtleBlue = "Couleur bleue"
PythonTurtleBrown = "Couleur marron"
PythonTurtleCircle = "Cercle de rayon r pixels"
PythonTurtleColor = "Modifie la couleur du tracé"
PythonTurtleColorMode = "Met le mode de couleur à 1.0 ou 255"
PythonTurtleForward = "Avance de x pixels"
PythonTurtleFunction = "Préfixe fonction du module turtle"
PythonTurtleGoto = "Va au point de coordonnées (x,y)"
PythonTurtleGreen = "Couleur verte"
PythonTurtleGrey = "Couleur grise"
PythonTurtleHeading = "Renvoie l'orientation actuelle"
PythonTurtleHideturtle = "Masque la tortue"
PythonTurtleIsdown = "True si le crayon est abaissé"
PythonTurtleLeft = "Pivote de a degrés vers la gauche"
PythonTurtleOrange = "Couleur orange"
PythonTurtlePendown = "Abaisse le crayon"
PythonTurtlePensize = "Taille du tracé en pixels"
PythonTurtlePenup = "Relève le crayon"
PythonTurtlePink = "Couleur rose"
PythonTurtlePosition = "Renvoie la position (x,y)"
PythonTurtlePurple = "Couleur violette"
PythonTurtleRed = "Couleur rouge"
PythonTurtleReset = "Réinitialise le dessin"
PythonTurtleRight = "Pivote de a degrés vers la droite"
PythonTurtleSetheading = "Met un cap de a degrés"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Affiche la tortue"
PythonTurtleSpeed = "Vitesse du tracé entre 0 et 10"
PythonTurtleWhite = "Couleur blanche"
PythonTurtleYellow = "Couleur jaune"
PythonTurtleWrite = "Affiche un texte"
PythonUniform = "Nombre décimal dans [a,b]"
PythonTimeFromImport = "Importation du module temps"
PythonTimeImport = "Importation du module temps"
PythonImportTime = "Importation du module temps"
PythonTimePrefix = "Préfixe fonction du module temps"
PythonTimeSleep = "Attendre n secondes"
PythonTimeMonotonic = "Retourne le temps monotonic"
PythonMonotonic = "Retourne le temps monotonic"
PythonFileOpen = "Ouvre un fichier"
PythonFileSeekable = "Indique si seek peut être utilisé"
PythonFileSeek = "Déplace le curseur interne"
PythonFileTell = "Donne la posititon du curseur"
PythonFileClose = "Ferme un fichier"
PythonFileClosed = "True si le fichier a été fermé"
PythonFileRead = "Lis jusqu'à size bytes"
PythonFileWrite = "Écris b dans le fichier"
PythonFileReadline = "Lis une ligne ou jusqu'à size bytes"
PythonFileReadlines = "Lis une liste de lignes"
PythonFileTruncate = "Redimensionne le fichier"
PythonFileWritelines = "Écris une liste de lignes"
PythonFileName = "Nom du fichier"
PythonFileMode = "Mode d'ouverture du fichier"
PythonFileReadable = "Indique si read peut être utilisé"
PythonFileWritable = "Indique si write peut être utilisé"

View File

@@ -27,6 +27,17 @@ PythonChoice = "Véletlenszerü szám a listában"
PythonClear = "A lista ürítése"
PythonCmathFunction = "cmath modul funkció elötag"
PythonColor = "Rgb szín meghatározása"
PythonColorBlack = "Fekete szín"
PythonColorBlue = "Kék szín"
PythonColorBrown = "Barna szín"
PythonColorGreen = "Zöld szín"
PythonColorGrey = "Szürke szín"
PythonColorOrange = "Narancssárga szín"
PythonColorPink = "Rózsaszín szín"
PythonColorPurple = "Lila szín"
PythonColorRed = "Piros szín"
PythonColorWhite = "White color"
PythonColorYellow = "Sárga szín"
PythonComplex = "A + ib visszaadása"
PythonCopySign = "Visszatérés x-val y jelével"
PythonCos = "Koszinusz"
@@ -162,39 +173,45 @@ PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "idömodul funkció elötag"
PythonTrunc = "x egészre csonkítva"
PythonTurtleBackward = "Visszalépés x pixelrel"
PythonTurtleBlack = "Fekete szín"
PythonTurtleBlue = "Kék szín"
PythonTurtleBrown = "Barna szín"
PythonTurtleCircle = "r pixel sugarú kör"
PythonTurtleColor = "Állítsa be az toll színét"
PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
PythonTurtleForward = "Ugrás x pixelrel"
PythonTurtleFunction = "teknös modul funkció elötag"
PythonTurtleGoto = "Mozgatás (x, y) koordinátákra"
PythonTurtleGreen = "Zöld szín"
PythonTurtleGrey = "Szürke szín"
PythonTurtleHeading = "Visszaadja az aktuális címsort"
PythonTurtleHideturtle = "A teknös elrejtése"
PythonTurtleIsdown = "Visszatérés igazhoz, ha az toll lefelé"
PythonTurtleLeft = "Forduljon fokkal balra"
PythonTurtleOrange = "Narancssárga szín"
PythonTurtlePendown = "Húzza le a tollat"
PythonTurtlePensize = "Állítsa a vonalvastagságot x pixelre"
PythonTurtlePenup = "Húzza fel a tollat"
PythonTurtlePink = "Rózsaszín szín"
PythonTurtlePosition = "Az aktuális (x, y) hely visszaadása"
PythonTurtlePurple = "Lila szín"
PythonTurtleRed = "Piros szín"
PythonTurtleReset = "A rajz visszaállítása"
PythonTurtleRight = "Forduljon fokkal jobbra"
PythonTurtleSetheading = "Állítsa be a tájolást fokokra"
PythonTurtleSetposition = "A helymeghatározás"
PythonTurtleShowturtle = "Mutasd a teknösöt"
PythonTurtleSpeed = "Rajzolási sebesség 0 és 10 között"
PythonTurtleWhite = "Fehér szín"
PythonTurtleYellow = "Sárga szín"
PythonTurtleWrite = "Display a text"
PythonUniform = "Lebegöpontos szám [a, b] -ben"
PythonTimeFromImport = "Idömodul importálása"
PythonTimeImport = "Idömodul importálása"
PythonImportTime = "Idömodul importálása"
PythonTimePrefix = "idömodul funkció elötag"
PythonTimeSleep = "Várj n másodpercet"
PythonTimeMonotonic = "Vissza a monoton idö"
PythonMonotonic = "Vissza a monoton idö"
PythonFileOpen = "Fájl megnyitása"
PythonFileSeekable = "A fájl kereshető?"
PythonFileSeek = "A fájl kurzorának áthelyezése"
PythonFileTell = "A fájl kurzorának helye"
PythonFileClose = "Bezár egy fájlt"
PythonFileClosed = "Igaz, ha a fájl bezárt"
PythonFileRead = "Olvasson méretbájtig"
PythonFileWrite = "B beírása fájlba"
PythonFileReadline = "Olvas egy sort"
PythonFileReadlines = "Olvassa a sorok listáját"
PythonFileTruncate = "A fájl átméretezése méretre"
PythonFileWritelines = "Sorok listáját írja"
PythonFileName = "a fájl neve"
PythonFileMode = "a fájl nyitott módja"
PythonFileReadable = "A fájl olvasható?"
PythonFileWritable = "A fájl írható?"

217
apps/code/catalog.it.i18n Normal file
View File

@@ -0,0 +1,217 @@
PythonPound = "Commento"
PythonPercent = "Modulo"
Python1J = "Unità immaginaria"
PythonLF = "Nuova riga"
PythonTab = "Tabulazione"
PythonAmpersand = "Congiunzione"
PythonSymbolExp = "Disgiunzione esclusiva"
PythonVerticalBar = "Disgiunzione"
PythonImag = "Parte immaginaria di z"
PythonReal = "Parte reale di z"
PythonSingleQuote = "Apostrofo"
PythonAbs = "Valore assoluto/Modulo"
PythonAcos = "Coseno d'arco"
PythonAcosh = "Coseno iperbolico inverso"
PythonAppend = "Inserisce x alla fine della lista"
PythonArrow = "Freccia da (x,y) a (x+dx,y+dy)"
PythonAsin = "Arco sinusoidale"
PythonAsinh = "Arco sinusoidale iperbolico"
PythonAtan = "Arco tangente"
PythonAtan2 = "Calcolo di atan(y/x)"
PythonAtanh = "Arco tangente iperbolico"
PythonAxis = "Imposta assi (xmin,xmax,ymin,ymax)"
PythonBar = "Grafico a barre con x valori"
PythonBin = "Converte un intero in binario"
PythonCeil = "Parte intera superiore"
PythonChoice = "Numero aleatorio nella lista"
PythonClear = "Svuota la lista"
PythonCmathFunction = "Funz. prefissata modulo cmath"
PythonColor = "Definisci un colore rvb"
PythonColorBlack = "Colore nero"
PythonColorBlue = "Colore blu"
PythonColorBrown = "Colore marrone"
PythonColorGreen = "Colore verde"
PythonColorGrey = "Colore grigio"
PythonColorOrange = "Colore arancione"
PythonColorPink = "Colore rosa"
PythonColorPurple = "Colore viola"
PythonColorRed = "Colore rosso"
PythonColorWhite = "Colore bianco"
PythonColorYellow = "Colore giallo"
PythonComplex = "Restituisce a+ib"
PythonCopySign = "Restituisce x con segno di y"
PythonCos = "Coseno"
PythonCosh = "Coseno iperbolico"
PythonCount = "Conta le ricorrenze di x"
PythonDegrees = "Conversione di radianti in gradi"
PythonDivMod = "Quoziente e resto"
PythonDrawString = "Visualizza il testo dal pixel x,y"
PythonErf = "Funzione d'errore"
PythonErfc = "Funzione d'errore complementare"
PythonEval = "Valuta l'espressione nell'argomento "
PythonExp = "Funzione esponenziale"
PythonExpm1 = "Calcola exp(x)-1"
PythonFabs = "Valore assoluto"
PythonFillRect = "Riempie un rettangolo"
PythonFloat = "Conversione in flottanti"
PythonFloor = "Parte intera"
PythonFmod = "a modulo b"
PythonFrExp = "Mantissa ed esponente di x : (m,e)"
PythonGamma = "Funzione gamma"
PythonGetPixel = "Restituisce colore del pixel(x,y)"
PythonGetrandbits = "Numero aleatorio con k bit"
PythonGrid = "Attiva la visibilità della griglia"
PythonHex = "Conversione intero in esadecimale"
PythonHist = "Disegna l'istogramma di x"
PythonImportCmath = "Importa modulo cmath"
PythonImportIon = "Importa modulo ion"
PythonImportKandinsky = "Importa modulo kandinsky"
PythonImportRandom = "Importa modulo random"
PythonImportMath = "Importa modulo math"
PythonImportMatplotlibPyplot = "Importa modulo matplotlib.pyplot"
PythonImportTurtle = "Importa del modulo turtle"
PythonImportTime = "Importa del modulo time"
PythonIndex = "Indice prima occorrenza di x"
PythonInput = "Inserire un valore"
PythonInsert = "Inserire x in posizione i-esima"
PythonInt = "Conversione in intero"
PythonIonFunction = "Prefisso di funzione modulo ion"
PythonIsFinite = "Testa se x è finito"
PythonIsInfinite = "Testa se x est infinito"
PythonIsKeyDown = "Restituisce True premendo tasto k"
PythonIsNaN = "Testa se x è NaN"
PythonKandinskyFunction = "Prefisso funzione modulo kandinsky"
PythonKeyLeft = "Tasto FRECCIA SINISTRA"
PythonKeyUp = "Tasto FRECCIA ALTO"
PythonKeyDown = "Tasto FRECCIA BASSO"
PythonKeyRight = "Tasto FRECCIA DESTRA"
PythonKeyOk = "Tasto OK"
PythonKeyBack = "Tasto INDIETRO"
PythonKeyHome = "Tasto CASA"
PythonKeyOnOff = "Tasto ON/OFF"
PythonKeyShift = "Tasto SHIFT"
PythonKeyAlpha = "Tasto ALPHA"
PythonKeyXnt = "Tasto X,N,T"
PythonKeyVar = "Tasto VAR"
PythonKeyToolbox = "Tasto TOOLBOX"
PythonKeyBackspace = "Tasto CANCELLA"
PythonKeyExp = "Tasto ESPONENZIALE"
PythonKeyLn = "Tasto LOGARITMO NEPERIANO"
PythonKeyLog = "Tasto LOGARITMO DECIMALE"
PythonKeyImaginary = "Tasto I IMMAGINE"
PythonKeyComma = "Tasto VIRGOLA"
PythonKeyPower = "Tasto POTENZA"
PythonKeySine = "Tasto SENO"
PythonKeyCosine = "Tasto COSENO"
PythonKeyTangent = "Tasto TANGENTE"
PythonKeyPi = "Tasto PI"
PythonKeySqrt = "Tasto RADICE QUADRATA"
PythonKeySquare = "Tasto QUADRATO"
PythonKeySeven = "Tasto 7"
PythonKeyEight = "Tasto 8"
PythonKeyNine = "Tasto 9"
PythonKeyLeftParenthesis = "Tasto PARENTESI SINISTRA"
PythonKeyRightParenthesis = "Tasto PARENTESI DESTRA"
PythonKeyFour = "Tasto 4"
PythonKeyFive = "Tasto 5"
PythonKeySix = "Tasto 6"
PythonKeyMultiplication = "Tasto MOLTIPLICAZIONE"
PythonKeyDivision = "Tasto DIVISIONE"
PythonKeyOne = "Tasto 1"
PythonKeyTwo = "Tasto 2"
PythonKeyThree = "Tasto 3"
PythonKeyPlus = "Tasto PIÙ"
PythonKeyMinus = "Tasto MENO"
PythonKeyZero = "Tasto 0"
PythonKeyDot = "Tasto PUNTO"
PythonKeyEe = "Tasto 10 POTENZA X"
PythonKeyAns = "Tasto ANS"
PythonKeyExe = "Tasto EXE"
PythonLdexp = "Inversa di frexp : x*(2**i)"
PythonLength = "Longhezza di un oggetto"
PythonLgamma = "Logaritmo della funzione gamma"
PythonLog = "Logaritmo di base a"
PythonLog10 = "Logaritmo decimale"
PythonLog2 = "Logaritmo di base 2"
PythonMathFunction = "Prefisso funzione del modulo math"
PythonMatplotlibPyplotFunction = "Prefisso modulo matplotlib.pyplot"
PythonMax = "Massimo"
PythonMin = "Minimo"
PythonModf = "Parti frazionarie e intere"
PythonMonotonic = "Restituisce il valore dell'orologio"
PythonOct = "Conversione in ottale"
PythonPhase = "Argomento di z"
PythonPlot = "Disegna y in f. di x come linee"
PythonPolar = "Conversione in polare"
PythonPop = "Cancella l'ultimo elemento"
PythonPower = "x alla potenza y"
PythonPrint = "Visualizza l'oggetto"
PythonRadians = "Conversione da gradi a radianti"
PythonRandint = "Intero aleatorio in [a,b]"
PythonRandom = "Numero aleatorio in [0,1["
PythonRandomFunction = "Prefisso funzione modulo casuale"
PythonRandrange = "Numero dentro il range(start, stop)"
PythonRangeStartStop = "Lista da start a stop-1"
PythonRangeStop = "Lista da 0 a stop-1"
PythonRect = "Converte in coordinate algebriche"
PythonRemove = "Cancella la prima x dalla lista"
PythonReverse = "Inverte gli elementi della lista"
PythonRound = "Arrotondato a n cifre decimali"
PythonScatter = "Diagramma dispersione y in f. di x"
PythonSeed = "Inizializza il generatore random"
PythonSetPixel = "Colora il pixel (x,y)"
PythonShow = "Mostra la figura"
PythonSin = "Seno"
PythonSinh = "Seno iperbolico"
PythonSleep = "Sospende l'esecuzione t secondi"
PythonSort = "Ordina l'elenco"
PythonSqrt = "Radice quadrata"
PythonSum = "Somma degli elementi della lista"
PythonTan = "Tangente"
PythonTanh = "Tangente iperbolica"
PythonText = "Mostra un testo in (x,y)"
PythonTimeFunction = "Prefisso funzione modulo time"
PythonTrunc = "Troncamento intero"
PythonTurtleBackward = "Indietreggia di x pixels"
PythonTurtleCircle = "Cerchio di raggio r pixel"
PythonTurtleColor = "Modifica il colore del tratto"
PythonTurtleColorMode = "Imposta modalità colore a 1.0 o 255"
PythonTurtleForward = "Avanza di x pixel"
PythonTurtleFunction = "Prefisso funzione modello turtle"
PythonTurtleGoto = "Spostati alle coordinate (x,y)"
PythonTurtleHeading = "Restituisce l'orientamento attuale"
PythonTurtleHideturtle = "Nascondi la tartaruga"
PythonTurtleIsdown = "True se la penna è abbassata"
PythonTurtleLeft = "Ruota di a gradi a sinistra"
PythonTurtlePendown = "Abbassa la penna"
PythonTurtlePensize = "Dimensione del tratto in pixel"
PythonTurtlePenup = "Solleva la penna"
PythonTurtlePosition = "Fornisce posizione corrente (x,y)"
PythonTurtleReset = "Azzera il disegno"
PythonTurtleRight = "Ruota di a gradi a destra"
PythonTurtleSetheading = "Imposta l'orientamento per a gradi"
PythonTurtleSetposition = "Posiziona la tartaruga"
PythonTurtleShowturtle = "Mostra la tartaruga"
PythonTurtleSpeed = "Velocità di disegno (x tra 0 e 10)"
PythonTurtleWrite = "Mostra un testo"
PythonUniform = "Numero decimale tra [a,b]"
PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
PythonTimeSleep = "Wait for n second"
PythonMonotonic = "Return monotonic time"
PythonFileOpen = "Opens a file"
PythonFileSeekable = "Tells if seek can be used on a file"
PythonFileSeek = "Move file's cursor"
PythonFileTell = "Get file's cursor location"
PythonFileClose = "Closes a file"
PythonFileClosed = "True if file was closed"
PythonFileRead = "Read up to size bytes"
PythonFileWrite = "Write b into file"
PythonFileReadline = "Reads a line or up to size bytes"
PythonFileReadlines = "Reads a list of lines"
PythonFileTruncate = "Resize the file to size"
PythonFileWritelines = "Writes a list of lines"
PythonFileName = "Contains file's name"
PythonFileMode = "Contains file's open mode"
PythonFileReadable = "Tells if read can be used on a file"
PythonFileWritable = "Tells if write can be used on a file"

217
apps/code/catalog.nl.i18n Normal file
View File

@@ -0,0 +1,217 @@
PythonPound = "Opmerkingen"
PythonPercent = "Modulo"
Python1J = "Imaginaire i"
PythonLF = "Nieuwe regel"
PythonTab = "Tabulatie"
PythonAmpersand = "Bitsgewijze en"
PythonSymbolExp = "Bitsgewijze exclusieve of"
PythonVerticalBar = "Bitsgewijze of"
PythonImag = "Imaginair deel van z"
PythonReal = "Reëel deel van z"
PythonSingleQuote = "Enkele aanhalingstekens"
PythonAbs = "Absolute waarde"
PythonAcos = "Arccosinus"
PythonAcosh = "Arccosinus hyperbolicus"
PythonAppend = "Voeg x toe aan het eind van je lijst"
PythonArrow = "Arrow from (x,y) to (x+dx,y+dy)"
PythonAsin = "Arcsinus"
PythonAsinh = "Arcsinus hyperbolicus"
PythonAtan = "Arctangens"
PythonAtan2 = "Geeft atan(y/x)"
PythonAtanh = "Arctangens hyperbolicus"
PythonAxis = "Set the axes to (xmin,xmax,ymin,ymax)"
PythonBar = "Draw a bar plot with x values"
PythonBin = "Zet integer om in een binair getal"
PythonCeil = "Plafond"
PythonChoice = "Geeft willek. getal van de lijst"
PythonClear = "Lijst leegmaken"
PythonCmathFunction = "cmath module voorvoegsel"
PythonColor = "Definieer een rgb kleur"
PythonColorBlack = "Zwarte kleur"
PythonColorBlue = "Blauwe kleur"
PythonColorBrown = "Bruine kleur"
PythonColorGreen = "Groene kleur"
PythonColorGrey = "Grijze kleur"
PythonColorOrange = "Oranje kleur"
PythonColorPink = "Roze kleur"
PythonColorPurple = "Paarse kleur"
PythonColorRed = "Rode kleur"
PythonColorWhite = "Witte kleur"
PythonColorYellow = "Gele kleur"
PythonComplex = "Geeft a+ib"
PythonCopySign = "Geeft x met het teken van y"
PythonCos = "Cosinus"
PythonCosh = "Cosinus hyperbolicus"
PythonCount = "Tel voorkomen van x"
PythonDegrees = "Zet x om van radialen naar graden"
PythonDivMod = "Quotiënt en rest"
PythonDrawString = "Geef een tekst weer van pixel (x,y)"
PythonErf = "Error functie"
PythonErfc = "Complementaire error functie"
PythonEval = "Geef de geëvalueerde uitdrukking"
PythonExp = "Exponentiële functie"
PythonExpm1 = "Bereken exp(x)-1"
PythonFabs = "Absolute waarde"
PythonFillRect = "Vul een rechthoek bij pixel (x,y)"
PythonFloat = "Zet x om in een float"
PythonFloor = "Vloer"
PythonFmod = "a modulo b"
PythonFrExp = "Mantisse en exponent van x: (m,e)"
PythonGamma = "Gammafunctie"
PythonGetPixel = "Geef pixel (x,y) kleur (rgb)"
PythonGetrandbits = "Integer met k willekeurige bits"
PythonGrid = "Toggle the visibility of the grid"
PythonHex = "Zet integer om in hexadecimaal"
PythonHist = "Draw the histogram of x"
PythonImportCmath = "Importeer cmath module"
PythonImportIon = "Importeer ion module"
PythonImportKandinsky = "Importeer kandinsky module"
PythonImportRandom = "Importeer random module"
PythonImportMath = "Importeer math module"
PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module"
PythonImportTime = "Importeer time module"
PythonImportTurtle = "Importeer turtle module"
PythonIndex = "Index van de eerste x aanwezigheden"
PythonInput = "Wijs een waarde toe"
PythonInsert = "Voeg x toe aan index i in de lijst"
PythonInt = "Zet x om in een integer"
PythonIonFunction = "ion module voorvoegsel"
PythonIsFinite = "Controleer of x eindig is"
PythonIsInfinite = "Controleer of x oneindig is"
PythonIsKeyDown = "Geef True als k toets omlaag is"
PythonIsNaN = "Controleer of x geen nummer is"
PythonKandinskyFunction = "kandinsky module voorvoegsel"
PythonKeyLeft = "PIJL NAAR LINKS toets"
PythonKeyUp = "PIJL OMHOOG toets"
PythonKeyDown = "PIJL OMLAAG toets"
PythonKeyRight = "PIJL NAAR RECHTS toets"
PythonKeyOk = "OK toets"
PythonKeyBack = "TERUG toets"
PythonKeyHome = "HOME toets"
PythonKeyOnOff = "AAN/UIT toets"
PythonKeyShift = "SHIFT toets"
PythonKeyAlpha = "ALPHA toets"
PythonKeyXnt = "X,N,T toets"
PythonKeyVar = "VAR toets"
PythonKeyToolbox = "TOOLBOX toets"
PythonKeyBackspace = "BACKSPACE toets"
PythonKeyExp = "EXPONENTIEEL toets"
PythonKeyLn = "NATUURLIJKE LOGARITME toets"
PythonKeyLog = "BRIGGSE LOGARITME toets"
PythonKeyImaginary = "IMAGINAIRE I toets"
PythonKeyComma = "KOMMA toets"
PythonKeyPower = "MACHT toets"
PythonKeySine = "SINUS toets"
PythonKeyCosine = "COSINUS toets"
PythonKeyTangent = "TANGENS toets"
PythonKeyPi = "PI toets"
PythonKeySqrt = "VIERKANTSWORTEL toets"
PythonKeySquare = "KWADRAAT toets"
PythonKeySeven = "7 toets"
PythonKeyEight = "8 toets"
PythonKeyNine = "9 toets"
PythonKeyLeftParenthesis = "HAAKJE OPENEN toets"
PythonKeyRightParenthesis = "HAAKJE SLUITEN toets"
PythonKeyFour = "4 toets"
PythonKeyFive = "5 toets"
PythonKeySix = "6 toets"
PythonKeyMultiplication = "VERMENIGVULDIGEN toets"
PythonKeyDivision = "DELEN toets"
PythonKeyOne = "1 toets"
PythonKeyTwo = "2 toets"
PythonKeyThree = "3 toets"
PythonKeyPlus = "PLUS toets"
PythonKeyMinus = "MIN toets"
PythonKeyZero = "0 toets"
PythonKeyDot = "PUNT toets"
PythonKeyEe = "10 TOT DE MACHT X toets"
PythonKeyAns = "ANS toets"
PythonKeyExe = "EXE toets"
PythonLdexp = "Geeft x*(2**i), inversie van frexp"
PythonLength = "Lengte van een object"
PythonLgamma = "Log-gammafunctie"
PythonLog = "Logaritme met grondgetal a"
PythonLog10 = "Logaritme met grondgetal 10"
PythonLog2 = "Logaritme met grondgetal 2"
PythonMathFunction = "math module voorvoegsel"
PythonMatplotlibPyplotFunction = "matplotlib.pyplot module prefix"
PythonMax = "Maximum"
PythonMin = "Minimum"
PythonModf = "Fractionele en gehele delen van x"
PythonMonotonic = "Waarde van een monotone klok"
PythonOct = "Integer omzetten naar octaal"
PythonPhase = "Fase van z in radialen"
PythonPlot = "Plot y versus x as lines"
PythonPolar = "z in poolcoördinaten"
PythonPop = "Verwijder en breng het laatste item terug"
PythonPower = "x tot de macht y"
PythonPrint = "Print object"
PythonRadians = "Zet x om van graden naar radialen"
PythonRandint = "Geeft willek. integer in [a,b]"
PythonRandom = "Een willekeurig getal in [0,1["
PythonRandomFunction = "random module voorvoegsel"
PythonRandrange = "Willek. getal in range(start, stop)"
PythonRangeStartStop = "Lijst van start tot stop-1"
PythonRangeStop = "Lijst van 0 tot stop-1"
PythonRect = "z in cartesiaanse coördinaten"
PythonRemove = "Verwijder het eerste voorkomen van x"
PythonReverse = "Keer de elementen van de lijst om"
PythonRound = "Rond af op n cijfers"
PythonScatter = "Draw a scatter plot of y versus x"
PythonSeed = "Start willek. getallengenerator"
PythonSetPixel = "Kleur pixel (x,y)"
PythonShow = "Display the figure"
PythonSin= "Sinus"
PythonSinh = "Sinus hyperbolicus"
PythonSleep = "Stel executie voor t seconden uit"
PythonSort = "Sorteer de lijst"
PythonSqrt = "Vierkantswortel"
PythonSum = "Sommeer de items van een lijst"
PythonTan = "Tangens"
PythonTanh = "Tangens hyperbolicus"
PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "time module voorvoegsel"
PythonTrunc = "x afgeknot tot een integer"
PythonTurtleBackward = "Ga achterwaarts met x pixels"
PythonTurtleCircle = "Cirkel van straal r pixels"
PythonTurtleColor = "Stel de kleur van de pen in"
PythonTurtleColorMode = "Stel de kleurmodus in op 1.0 of 255"
PythonTurtleForward = "Ga voorwaarts met x pixels"
PythonTurtleFunction = "turtle module voorvoegsel"
PythonTurtleGoto = "Verplaats naar (x,y) coordinaten"
PythonTurtleHeading = "Ga terug naar de huidige koers"
PythonTurtleHideturtle = "Verberg de schildpad"
PythonTurtleIsdown = "Geeft True als pen naar beneden is"
PythonTurtleLeft = "Ga linksaf met a graden"
PythonTurtlePendown = "Zet de pen naar beneden"
PythonTurtlePensize = "Stel de lijndikte in op x pixels"
PythonTurtlePenup = "Zet de pen omhoog"
PythonTurtlePosition = "Zet huidige (x,y) locatie terug"
PythonTurtleReset = "Reset de tekening"
PythonTurtleRight = "Ga rechtsaf met a graden"
PythonTurtleSetheading = "Zet de oriëntatie op a graden"
PythonTurtleSetposition = "Plaats de schildpad"
PythonTurtleShowturtle = "Laat de schildpad zien"
PythonTurtleSpeed = "Tekensnelheid tussen 0 and 10"
PythonTurtleWrite = "Display a text"
PythonUniform = "Zwevendekommagetal in [a,b]"
PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
PythonTimeSleep = "Wait for n second"
PythonMonotonic = "Return monotonic time"
PythonFileOpen = "Opens a file"
PythonFileSeekable = "Tells if seek can be used on a file"
PythonFileSeek = "Move file's cursor"
PythonFileTell = "Get file's cursor location"
PythonFileClose = "Closes a file"
PythonFileClosed = "True if file was closed"
PythonFileRead = "Read up to size bytes"
PythonFileWrite = "Write b into file"
PythonFileReadline = "Reads a line or up to size bytes"
PythonFileReadlines = "Reads a list of lines"
PythonFileTruncate = "Resize the file to size"
PythonFileWritelines = "Writes a list of lines"
PythonFileName = "Contains file's name"
PythonFileMode = "Contains file's open mode"
PythonFileReadable = "Tells if read can be used on a file"
PythonFileWritable = "Tells if write can be used on a file"

View File

@@ -1,200 +1,217 @@
PythonPound = "Comment"
PythonPercent = "Modulo"
Python1J = "Imaginary i"
PythonLF = "Line feed"
PythonTab = "Tabulation"
PythonAmpersand = "Bitwise and"
PythonSymbolExp = "Bitwise exclusive or"
PythonVerticalBar = "Bitwise or"
PythonSingleQuote = "Single quote"
PythonImag = "Imaginary part of z"
PythonReal = "Real part of z"
PythonAbs = "Absolute value/Magnitude"
PythonAcos = "Arc cosine"
PythonAcosh = "Arc hyperbolic cosine"
PythonAppend = "Add x to the end of the list"
PythonArrow = "Arrow from (x,y) to (x+dx,y+dy)"
PythonAsin = "Arc sine"
PythonAsinh = "Arc hyperbolic sine"
PythonAtan = "Arc tangent"
PythonAtan2 = "Return atan(y/x)"
PythonAtanh = "Arc hyperbolic tangent"
PythonAxis = "Set axes to (xmin,xmax,ymin,ymax)"
PythonBar = "Draw a bar plot with x values"
PythonBin = "Convert integer to binary"
PythonCeil = "Ceiling"
PythonChoice = "Random number in the list"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath module function prefix"
PythonColor = "Define a rgb color"
PythonComplex = "Return a+ib"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Cosine"
PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawString = "Display a text from pixel (x,y)"
PythonErf = "Error function"
PythonErfc = "Complementary error function"
PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonFloor = "Floor"
PythonFmod = "a modulo b"
PythonFrExp = "Mantissa and exponent of x"
PythonGamma = "Gamma function"
PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
PythonGrid = "Toggle the visibility of the grid"
PythonHex = "Convert integer to hexadecimal"
PythonHist = "Draw the histogram of x"
PythonImportCmath = "Import cmath module"
PythonImportIon = "Import ion module"
PythonImportKandinsky = "Import kandinsky module"
PythonImportRandom = "Import random module"
PythonImportMath = "Import math module"
PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module"
PythonImportTime = "Import time module"
PythonImportTurtle = "Import turtle module"
PythonIndex = "Index of the first x occurrence"
PythonInput = "Prompt a value"
PythonInsert = "Insert x at index i in the list"
PythonInt = "Convert x to an integer"
PythonIonFunction = "ion module function prefix"
PythonIsFinite = "Check if x is finite"
PythonIsInfinite = "Check if x is infinity"
PythonIsKeyDown = "Return True if the k key is down"
PythonIsNaN = "Check if x is a NaN"
PythonKandinskyFunction = "kandinsky module function prefix"
PythonKeyLeft = "LEFT ARROW key"
PythonKeyUp = "UP ARROW key"
PythonKeyDown = "DOWN ARROW key"
PythonKeyRight = "RIGHT ARROW key"
PythonKeyOk = "OK key"
PythonKeyBack = "BACK key"
PythonKeyHome = "HOME key"
PythonKeyOnOff = "ON/OFF key"
PythonKeyShift = "SHIFT key"
PythonKeyAlpha = "ALPHA key"
PythonKeyXnt = "X,N,T key"
PythonKeyVar = "VAR key"
PythonKeyToolbox = "TOOLBOX key"
PythonKeyBackspace = "BACKSPACE key"
PythonKeyExp = "EXPONENTIAL key"
PythonKeyLn = "NATURAL LOGARITHM key"
PythonKeyLog = "DECIMAL LOGARITHM key"
PythonKeyImaginary = "IMAGINARY I key"
PythonKeyComma = "COMMA key"
PythonKeyPower = "POWER key"
PythonKeySine = "SINE key"
PythonKeyCosine = "COSINE key"
PythonKeyTangent = "TANGENT key"
PythonKeyPi = "PI key"
PythonKeySqrt = "SQUARE ROOT key"
PythonKeySquare = "SQUARE key"
PythonKeySeven = "7 key"
PythonKeyEight = "8 key"
PythonKeyNine = "9 key"
PythonKeyLeftParenthesis = "LEFT PARENTHESIS key"
PythonKeyRightParenthesis = "RIGHT PARENTHESIS key"
PythonKeyFour = "4 key"
PythonKeyFive = "5 key"
PythonKeySix = "6 key"
PythonKeyMultiplication = "MULTIPLICATION key"
PythonKeyDivision = "DIVISION key"
PythonKeyOne = "1 key"
PythonKeyTwo = "2 key"
PythonKeyThree = "3 key"
PythonKeyPlus = "PLUS key"
PythonKeyMinus = "MINUS key"
PythonKeyZero = "0 key"
PythonKeyDot = "DOT key"
PythonKeyEe = "10 POWER X key"
PythonKeyAns = "ANS key"
PythonKeyExe = "EXE key"
PythonLdexp = "Return x*(2**i), inverse of frexp"
PythonLength = "Length of an object"
PythonLgamma = "Log-gamma function"
PythonLog = "Logarithm to base a"
PythonLog10 = "Logarithm to base 10"
PythonLog2 = "Logarithm to base 2"
PythonMathFunction = "math module function prefix"
PythonMatplotlibPyplotFunction = "matplotlib.pyplot module prefix"
PythonMax = "Maximum"
PythonMin = "Minimum"
PythonModf = "Fractional and integer parts of x"
PythonMonotonic = "Value of a monotonic clock"
PythonOct = "Convert integer to octal"
PythonPhase = "Phase of z"
PythonPlot = "Plot y versus x as lines"
PythonPolar = "z in polar coordinates"
PythonPop = "Remove and return the last item"
PythonPower = "x raised to the power y"
PythonPrint = "Print object"
PythonRadians = "Convert x from degrees to radians"
PythonRandint = "Random integer in [a,b]"
PythonRandom = "Floating point number in [0,1["
PythonRandomFunction = "random module function prefix"
PythonRandrange = "Random number in range(start, stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
PythonRect = "z in cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
PythonScatter = "Draw a scatter plot of y versus x"
PythonSeed = "Initialize random number generator"
PythonSetPixel = "Color pixel (x,y)"
PythonShow = "Display the figure"
PythonSin = "Sine"
PythonSinh = "Hyperbolic sine"
PythonSleep = "Suspend the execution for t seconds"
PythonSort = "Sort the list"
PythonSqrt = "Square root"
PythonSum = "Sum the items of a list"
PythonTan = "Tangent"
PythonTanh = "Hyperbolic tangent"
PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Black color"
PythonTurtleBlue = "Blue color"
PythonTurtleBrown = "Brown color"
PythonTurtleCircle = "Circle of radius r pixels"
PythonTurtleColor = "Set the pen color"
PythonTurtleForward = "Move forward by x pixels"
PythonTurtleFunction = "turtle module function prefix"
PythonTurtleGoto = "Move to (x,y) coordinates"
PythonTurtleGreen = "Green color"
PythonTurtleGrey = "Grey color"
PythonTurtleHeading = "Return the current heading"
PythonTurtleHideturtle = "Hide the turtle"
PythonTurtleIsdown = "Return True if the pen is down"
PythonTurtleLeft = "Turn left by a degrees"
PythonTurtleOrange = "Orange color"
PythonTurtlePendown = "Pull the pen down"
PythonTurtlePensize = "Set the line thickness to x pixels"
PythonTurtlePenup = "Pull the pen up"
PythonTurtlePink = "Pink color"
PythonTurtlePosition = "Return the current (x,y) location"
PythonTurtlePurple = "Purple color"
PythonTurtleRed = "Red color"
PythonTurtleReset = "Reset the drawing"
PythonTurtleRight = "Turn right by a degrees"
PythonTurtleSetheading = "Set the orientation to a degrees"
PythonPound = "Comentário"
PythonPercent = "Módulo"
Python1J = "i Complexo"
PythonLF = "Nova linha"
PythonTab = "Tabulação"
PythonAmpersand = "Operador binário and"
PythonSymbolExp = "Operador binário exclusivo or"
PythonVerticalBar = "Operador binário or"
PythonSingleQuote = "Apóstrofo"
PythonImag = "Parte imaginária de z"
PythonReal = "Parte real de z"
PythonAbs = "Valor absoluto/módulo"
PythonAcos = "Arco cosseno"
PythonAcosh = "Arco cosseno hiperbólico"
PythonAppend = "Adicionar x no fim da lista"
PythonArrow = "Seta de (x,y) para (x+dx,y+dy)"
PythonAsin = "Arco seno"
PythonAsinh = "Arco seno hiperbólico"
PythonAtan = "Arco tangente"
PythonAtan2 = "Cálculo de atan(y/x)"
PythonAtanh = "Arco tangente hiperbólica"
PythonAxis = "Definir eixos (xmin,xmax,ymin,ymax)"
PythonBar = "Gráfico de barras com valores de x"
PythonBin = "Converter número inteiro em binário"
PythonCeil = "Teto"
PythonChoice = "Número aleatório na lista"
PythonClear = "Esvaziar a lista"
PythonCmathFunction = "Prefixo da função do módulo cmath"
PythonColor = "Define uma cor rgb"
PythonColorBlack = "Cor preta"
PythonColorBlue = "Cor azul"
PythonColorBrown = "Cor castanha"
PythonColorGreen = "Cor verde"
PythonColorGrey = "Cor cinzenta"
PythonColorOrange = "Cor laranja"
PythonColorPink = "Cor rosa"
PythonColorPurple = "Cor roxa"
PythonColorRed = "Cor vermelha"
PythonColorWhite = "Cor branca"
PythonColorYellow = "Cor amarela"
PythonComplex = "Devolve a+ib"
PythonCopySign = "Devolve x com o sinal de y"
PythonCos = "Cosseno"
PythonCosh = "Cosseno hiperbólico"
PythonCount = "Contar as ocorrências de x"
PythonDegrees = "Converter x de radianos para graus"
PythonDivMod = "Quociente e resto"
PythonDrawString = "Mostrar o texto do pixel (x,y)"
PythonErf = "Função erro"
PythonErfc = "Função erro complementar"
PythonEval = "Devolve a expressão avaliada"
PythonExp = "Função exponencial"
PythonExpm1 = "Calcular exp(x)-1"
PythonFabs = "Valor absoluto"
PythonFillRect = "Preencher um retângulo em (x,y)"
PythonFloat = "Converter x num flutuante"
PythonFloor = "Parte inteira"
PythonFmod = "a módulo b"
PythonFrExp = "Coeficiente e expoente de x: (m, e)"
PythonGamma = "Função gama"
PythonGetPixel = "Devolve a cor do pixel (x,y)"
PythonGetrandbits = "Número inteiro aleatório com k bits"
PythonGrid = "Alterar visibilidade da grelha"
PythonHex = "Converter inteiro em hexadecimal"
PythonHist = "Desenhar o histograma de x"
PythonImportCmath = "Importar módulo cmath"
PythonImportIon = "Importar módulo ion"
PythonImportKandinsky = "Importar módulo kandinsky"
PythonImportRandom = "Importar módulo random"
PythonImportMath = "Importar módulo math"
PythonImportMatplotlibPyplot = "Importar módulo matplotlib.pyplot"
PythonImportTime = "Importar módulo time"
PythonImportTurtle = "Importar módulo turtle"
PythonIndex = "Índice da primeira ocorrência de x"
PythonInput = "Adicionar um valor"
PythonInsert = "Inserir x no índice i na lista"
PythonInt = "Converter x num número inteiro"
PythonIonFunction = "Prefixo da função do módulo ion"
PythonIsFinite = "Verificar se x é finito"
PythonIsInfinite = "Verificar se x é infinito"
PythonIsKeyDown = "Devolve True se tecla k pressionada"
PythonIsNaN = "Verificar se x é um NaN"
PythonKandinskyFunction = "Prefixo da função do módulo kandinsky"
PythonKeyLeft = "tecla SETA ESQUERDA"
PythonKeyUp = "tecla SETA CIMA "
PythonKeyDown = "tecla SETA BAIXO"
PythonKeyRight = "tecla SETA DIREITA"
PythonKeyOk = "tecla OK"
PythonKeyBack = "tecla VOLTAR"
PythonKeyHome = "tecla HOME"
PythonKeyOnOff = "tecla ON/OFF"
PythonKeyShift = "tecla SHIFT"
PythonKeyAlpha = "tecla ALPHA"
PythonKeyXnt = "tecla X,N,T"
PythonKeyVar = "tecla VAR"
PythonKeyToolbox = "tecla CAIXA DE FERRAMENTAS"
PythonKeyBackspace = "tecla APAGAR"
PythonKeyExp = "tecla EXPONENCIAL"
PythonKeyLn = "tecla LOGARITMO NATURAL"
PythonKeyLog = "tecla LOGARITMO DECIMAL"
PythonKeyImaginary = "tecla I IMAGINÁRIO"
PythonKeyComma = "tecla VÍRGULA"
PythonKeyPower = "tecla EXPOENTE"
PythonKeySine = "tecla SENO"
PythonKeyCosine = "tecla COSSENO"
PythonKeyTangent = "tecla TANGENTE"
PythonKeyPi = "tecla PI"
PythonKeySqrt = "tecla RAIZ QUADRADA"
PythonKeySquare = "tecla AO QUADRADO"
PythonKeySeven = "tecla 7"
PythonKeyEight = "tecla 8"
PythonKeyNine = "tecla 9"
PythonKeyLeftParenthesis = "tecla PARÊNTESE ESQUERDO"
PythonKeyRightParenthesis = "tecla PARÊNTESE DIREITO"
PythonKeyFour = "tecla 4"
PythonKeyFive = "tecla 5"
PythonKeySix = "tecla 6"
PythonKeyMultiplication = "tecla MULTIPLICAÇÃO"
PythonKeyDivision = "tecla DIVISÃO"
PythonKeyOne = "tecla 1"
PythonKeyTwo = "tecla 2"
PythonKeyThree = "tecla 3"
PythonKeyPlus = "tecla MAIS"
PythonKeyMinus = "tecla MENOS"
PythonKeyZero = "tecla 0"
PythonKeyDot = "tecla PONTO"
PythonKeyEe = "tecla 10 expoente X"
PythonKeyAns = "tecla ANS"
PythonKeyExe = "tecla EXE"
PythonLdexp = "Devolve x*(2**i), inverso de frexp"
PythonLength = "Comprimento de um objeto"
PythonLgamma = "Logaritmo da função gama"
PythonLog = "Logaritmo de base a"
PythonLog10 = "Logaritmo de base 10"
PythonLog2 = "Logaritmo de base 2"
PythonMathFunction = "Prefixo da função do módulo math"
PythonMatplotlibPyplotFunction = "Prefixo do módulo matplotlib.pyplot"
PythonMax = "Máximo"
PythonMin = "Mínimo"
PythonModf = "Partes inteira e frácionária de x"
PythonMonotonic = "Devolve o valor do relógio"
PythonOct = "Converter número inteiro em octal"
PythonPhase = "Argumento de z"
PythonPlot = "Desenhar y em função de x"
PythonPolar = "z em coordenadas polares"
PythonPop = "Remover o último item"
PythonPower = "x levantado a y"
PythonPrint = "Mostrar o objeto"
PythonRadians = "Converter x de graus para radianos"
PythonRandint = "Número inteiro aleatório em [a,b]"
PythonRandom = "Número decimal em [0,1["
PythonRandomFunction = "Prefixo da função do módulo random"
PythonRandrange = "Número aleatório em [start,stop-1]"
PythonRangeStartStop = "Lista de start a stop-1"
PythonRangeStop = "Lista de 0 a stop-1"
PythonRect = "Converter para coordenadas cartesianas"
PythonRemove = "Remover a primeira ocorrência de x"
PythonReverse = "Inverter os elementos da lista"
PythonRound = "Arredondar para n dígitos"
PythonScatter = "Gráfico de dispersão (x,y)"
PythonSeed = "Iniciar gerador aleatório"
PythonSetPixel = "Cor do pixel (x,y)"
PythonShow = "Mostrar a figura"
PythonSin = "Seno"
PythonSinh = "Seno hiperbólico"
PythonSleep = "Suspender a execução por t segundos"
PythonSort = "Ordenar a lista"
PythonSqrt = "Raiz quadrada"
PythonSum = "Soma dos itens da lista"
PythonTan = "Tangente"
PythonTanh = "Tangente hiperbólica"
PythonText = "Mostrar um texto em (x,y)"
PythonTimeFunction = "Prefixo da função do módulo time"
PythonTrunc = "x truncado a um número inteiro"
PythonTurtleBackward = "Recuar x pixels"
PythonTurtleCircle = "Circunferência de raio r pixels"
PythonTurtleColor = "Definir a cor da caneta"
PythonTurtleColorMode = "Define modo de cor para 1.0 ou 255"
PythonTurtleForward = "Avançar x pixels"
PythonTurtleFunction = "Prefixo da função do módulo turtle"
PythonTurtleGoto = "Ir paras as coordenadas (x,y)"
PythonTurtleHeading = "Voltar para a orientação atual"
PythonTurtleHideturtle = "Esconder o turtle"
PythonTurtleIsdown = "True se a caneta está pressionada"
PythonTurtleLeft = "Vira à esquerda por a graus"
PythonTurtlePendown = "Puxar a caneta para baixo"
PythonTurtlePensize = "Definir a espessura para x pixels"
PythonTurtlePenup = "Puxar a caneta para cima"
PythonTurtlePosition = "Devolve a posição atual (x,y)"
PythonTurtleReset = "Reiniciar o desenho"
PythonTurtleRight = "Virar à esquerda por a graus"
PythonTurtleSetheading = "Definir a orientação por a graus"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Show the turtle"
PythonTurtleSpeed = "Drawing speed between 0 and 10"
PythonTurtleWhite = "White color"
PythonTurtleYellow = "Yellow color"
PythonUniform = "Floating point number in [a,b]"
PythonTimeFromImport = "Import time module"
PythonTimeImport = "Import time module"
PythonTurtleShowturtle = "Mostrar o turtle"
PythonTurtleSpeed = "Velocidade do desenho entre 0 e 10"
PythonTurtleWrite = "Mostrar um texto"
PythonUniform = "Número decimal em [a,b]"
PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
PythonTimeSleep = "Aguardar n segundos"
PythonTimeMonotonic = "Retornar tempo monotônico"
PythonTimeSleep = "Wait for n second"
PythonMonotonic = "Return monotonic time"
PythonFileOpen = "Opens a file"
PythonFileSeekable = "Tells if seek can be used on a file"
PythonFileSeek = "Move file's cursor"
PythonFileTell = "Get file's cursor location"
PythonFileClose = "Closes a file"
PythonFileClosed = "True if file was closed"
PythonFileRead = "Read up to size bytes"
PythonFileWrite = "Write b into file"
PythonFileReadline = "Reads a line or up to size bytes"
PythonFileReadlines = "Reads a list of lines"
PythonFileTruncate = "Resize the file to size"
PythonFileWritelines = "Writes a list of lines"
PythonFileName = "Contains file's name"
PythonFileMode = "Contains file's open mode"
PythonFileReadable = "Tells if read can be used on a file"
PythonFileWritable = "Tells if write can be used on a file"

View File

@@ -29,6 +29,17 @@ PythonCommandClearWithoutArg = ".clear()"
PythonCommandCmathFunction = "cmath.function"
PythonCommandCmathFunctionWithoutArg = "cmath.\x11"
PythonCommandColor = "color(r,g,b)"
PythonCommandColorBlack = "'black'"
PythonCommandColorBlue = "'blue'"
PythonCommandColorBrown = "'brown'"
PythonCommandColorGreen = "'green'"
PythonCommandColorGrey = "'grey'"
PythonCommandColorOrange = "'orange'"
PythonCommandColorPink = "'pink'"
PythonCommandColorPurple = "'purple'"
PythonCommandColorRed = "'red'"
PythonCommandColorWhite = "'white'"
PythonCommandColorYellow = "'yellow'"
PythonCommandComplex = "complex(a,b)"
PythonCommandConstantPi = "pi"
PythonCommandCopySign = "copysign(x,y)"
@@ -154,7 +165,7 @@ PythonCommandModf = "modf(x)"
PythonCommandMonotonic = "monotonic()"
PythonCommandOct = "oct(x)"
PythonCommandPhase = "phase(z)"
PythonCommandPlot = "plot(x,y)"
PythonCommandPlot = "plot(x,y,color)"
PythonCommandPolar = "polar(z)"
PythonCommandPop = "list.pop()"
PythonCommandPopWithoutArg = ".pop()"
@@ -165,17 +176,17 @@ PythonCommandRandint = "randint(a,b)"
PythonCommandRandom = "random()"
PythonCommandRandomFunction = "random.function"
PythonCommandRandomFunctionWithoutArg = "random.\x11"
PythonCommandRandrange = "randrange(start, stop)"
PythonCommandRangeStartStop = "range(start, stop)"
PythonCommandRandrange = "randrange(start,stop)"
PythonCommandRangeStartStop = "range(start,stop)"
PythonCommandRangeStop = "range(stop)"
PythonCommandReal = "z.real"
PythonCommandRealWithoutArg = ".real"
PythonCommandRect = "rect(r, arg)"
PythonCommandRect = "rect(r,arg)"
PythonCommandRemove = "list.remove(x)"
PythonCommandRemoveWithoutArg = ".remove(\x11)"
PythonCommandReverse = "list.reverse()"
PythonCommandReverseWithoutArg = ".reverse()"
PythonCommandRound = "round(x, n)"
PythonCommandRound = "round(x,n)"
PythonCommandScatter = "scatter(x,y)"
PythonCommandSeed = "seed(x)"
PythonCommandSetPixel = "set_pixel(x,y,color)"
@@ -202,32 +213,23 @@ PythonCommandUniform = "uniform(a,b)"
PythonConstantE = "2.718281828459045"
PythonConstantPi = "3.141592653589793"
PythonTurtleCommandBackward = "backward(x)"
PythonTurtleCommandBlack = "'black'"
PythonTurtleCommandBlue = "'blue'"
PythonTurtleCommandBrown = "'brown'"
PythonTurtleCommandCircle = "circle(r)"
PythonTurtleCommandColor = "color('c')/color(r,g,b)"
PythonTurtleCommandColorWithoutArg = "color(\x11)"
PythonTurtleCommandColor = "color('c')"
PythonTurtleCommandColorMode = "colormode(x)"
PythonTurtleCommandForward = "forward(x)"
PythonTurtleCommandGoto = "goto(x,y)"
PythonTurtleCommandGreen = "'green'"
PythonTurtleCommandGrey = "'grey'"
PythonTurtleCommandHeading = "heading()"
PythonTurtleCommandHideturtle = "hideturtle()"
PythonTurtleCommandIsdown= "isdown()"
PythonTurtleCommandLeft = "left(a)"
PythonTurtleCommandOrange = "'orange'"
PythonTurtleCommandPendown = "pendown()"
PythonTurtleCommandPensize = "pensize(x)"
PythonTurtleCommandPenup = "penup()"
PythonTurtleCommandPink = "'pink'"
PythonTurtleCommandPosition = "position()"
PythonTurtleCommandPurple = "'purple'"
PythonTurtleCommandRed = "'red'"
PythonTurtleCommandReset = "reset()"
PythonTurtleCommandRight = "right(a)"
PythonTurtleCommandSetheading = "setheading(a)"
PythonTurtleCommandSetposition = "setposition(x, [y])"
PythonTurtleCommandSetposition = "setposition(x,[y])"
PythonTurtleCommandShowturtle = "showturtle()"
PythonTurtleCommandSpeed = "speed(x)"
PythonTurtleCommandWhite = "'white'"
@@ -237,3 +239,36 @@ PythonTimeCommandImportFrom = "from time import *"
PythonTimeCommandSleep = "sleep()"
PythonTimeCommandSleepDemo = "sleep(n)"
PythonTimeCommandMonotonic = "monotonic()"
PythonCommandFileOpen = "open(name, [mode])"
PythonCommandFileOpenWithoutArg = "open(\x11)"
PythonCommandFileSeek = "file.seek(offset, [whence])"
PythonCommandFileSeekWithoutArg = ".seek(\x11)"
PythonCommandFileTell = "file.tell()"
PythonCommandFileTellWithoutArg = ".tell()"
PythonCommandFileSeekable = "file.seekable()"
PythonCommandFileSeekableWithoutArg = ".seekable()"
PythonCommandFileClose = "file.close()"
PythonCommandFileCloseWithoutArg = ".close()"
PythonCommandFileClosed = "file.closed"
PythonCommandFileClosedWithoutArg = ".closed"
PythonCommandFileRead = "file.read([size])"
PythonCommandFileReadWithoutArg = ".read(\x11)"
PythonCommandFileWrite = "file.write(b)"
PythonCommandFileWriteWithoutArg = ".write(\x11)"
PythonCommandFileReadline = "file.readline([size])"
PythonCommandFileReadlineWithoutArg = ".readline(\x11)"
PythonCommandFileReadlines = "file.readlines([hint])"
PythonCommandFileReadlinesWithoutArg = ".readlines(\x11)"
PythonCommandFileTruncate = "file.truncate([size])"
PythonCommandFileTruncateWithoutArg = ".truncate(\x11)"
PythonCommandFileWritelines = "file.writelines(lines)"
PythonCommandFileWritelinesWithoutArg = ".writelines(\x11)"
PythonCommandFileName = "file.name"
PythonCommandFileNameWithoutArg = ".name"
PythonCommandFileMode = "file.mode"
PythonCommandFileModeWithoutArg = ".mode"
PythonCommandFileReadable = "file.readable()"
PythonCommandFileReadableWithoutArg = ".readable()"
PythonCommandFileWritable = "file.writable()"
PythonCommandFileWritableWithoutArg = ".writable()"
PythonTurtleCommandWrite = "write(\"text\")"

View File

@@ -3,6 +3,7 @@
#include "script.h"
#include "variable_box_controller.h"
#include <apps/i18n.h>
#include <algorithm>
#include <assert.h>
#include <escher/metric.h>
#include <poincare/preferences.h>
@@ -16,8 +17,6 @@ extern "C" {
namespace Code {
static inline int minInt(int x, int y) { return x < y ? x : y; }
static const char * sStandardPromptText = ">>> ";
ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore
@@ -32,7 +31,7 @@ ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDe
m_pythonDelegate(pythonDelegate),
m_importScriptsWhenViewAppears(false),
m_selectableTableView(this, this, this, this),
m_editCell(this, pythonDelegate, this),
m_editCell(this, this, this),
m_scriptStore(scriptStore),
m_sandboxController(this),
m_inputRunLoopActive(false)
@@ -49,17 +48,13 @@ ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDe
}
bool ConsoleController::loadPythonEnvironment() {
if (m_pythonDelegate->isPythonUser(this)) {
return true;
if (!m_pythonDelegate->isPythonUser(this)) {
m_scriptStore->clearConsoleFetchInformation();
emptyOutputAccumulationBuffer();
m_pythonDelegate->initPythonWithUser(this);
MicroPython::registerScriptProvider(m_scriptStore);
m_importScriptsWhenViewAppears = m_autoImportScripts;
}
emptyOutputAccumulationBuffer();
m_pythonDelegate->initPythonWithUser(this);
MicroPython::registerScriptProvider(m_scriptStore);
m_importScriptsWhenViewAppears = m_autoImportScripts;
/* We load functions and variables names in the variable box before running
* any other python code to avoid failling to load functions and variables
* due to memory exhaustion. */
App::app()->variableBoxController()->loadFunctionsAndVariables();
return true;
}
@@ -291,7 +286,7 @@ void ConsoleController::willDisplayCellAtLocation(HighlightCell * cell, int i, i
}
}
void ConsoleController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
void ConsoleController::tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
if (withinTemporarySelection) {
return;
}
@@ -376,6 +371,14 @@ bool ConsoleController::textFieldDidAbortEditing(TextField * textField) {
return true;
}
VariableBoxController * ConsoleController::variableBoxForInputEventHandler(InputEventHandler * textInput) {
VariableBoxController * varBox = App::app()->variableBoxController();
varBox->loadVariablesImportedFromScripts();
varBox->setTitle(I18n::Message::FunctionsAndVariables);
varBox->setDisplaySubtitles(false);
return varBox;
}
void ConsoleController::resetSandbox() {
if (stackViewController()->topViewController() != sandbox()) {
return;
@@ -448,7 +451,7 @@ void ConsoleController::printText(const char * text, size_t length) {
flushOutputAccumulationBufferToStore();
micropython_port_vm_hook_refresh_print();
}
#if __EMSCRIPTEN__
// #if __EMSCRIPTEN__
/* If we called micropython_port_interrupt_if_needed here, we would need to
* put in the WHITELIST all the methods that call
* ConsoleController::printText, which means all the MicroPython methods that
@@ -461,13 +464,17 @@ void ConsoleController::printText(const char * text, size_t length) {
* device.
*
* TODO: Allow print interrpution on emscripten -> maybe by using WASM=1 ? */
#else
/*
* This can be run in Omega, since it uses WebASM.
*/
// #else
/* micropython_port_vm_hook_loop is not enough to detect user interruptions,
* because it calls micropython_port_interrupt_if_needed every 20000
* operations, and a print operation is quite long. We thus explicitely call
* micropython_port_interrupt_if_needed here. */
micropython_port_interrupt_if_needed();
#endif
// #endif
}
void ConsoleController::autoImportScript(Script script, bool force) {
@@ -476,7 +483,7 @@ void ConsoleController::autoImportScript(Script script, bool force) {
* the sandbox. */
hideAnyDisplayedViewController();
if (script.importationStatus() || force) {
if (script.autoImportationStatus() || force) {
// Step 1 - Create the command "from scriptName import *".
assert(strlen(k_importCommand1) + strlen(script.fullName()) - strlen(ScriptStore::k_scriptExtension) - 1 + strlen(k_importCommand2) + 1 <= k_maxImportCommandSize);
@@ -488,7 +495,7 @@ void ConsoleController::autoImportScript(Script script, bool force) {
/* Copy the script name without the extension ".py". The '.' is overwritten
* by the null terminating char. */
int copySizeWithNullTerminatingZero = minInt(k_maxImportCommandSize - currentChar, strlen(scriptName) - strlen(ScriptStore::k_scriptExtension));
int copySizeWithNullTerminatingZero = std::min(k_maxImportCommandSize - currentChar, strlen(scriptName) - strlen(ScriptStore::k_scriptExtension));
assert(copySizeWithNullTerminatingZero >= 0);
assert(copySizeWithNullTerminatingZero <= k_maxImportCommandSize - currentChar);
strlcpy(command+currentChar, scriptName, copySizeWithNullTerminatingZero);

View File

@@ -10,12 +10,14 @@
#include "console_store.h"
#include "sandbox_controller.h"
#include "script_store.h"
#include "variable_box_controller.h"
#include "../shared/input_event_handler_delegate.h"
namespace Code {
class App;
class ConsoleController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public TextFieldDelegate, public MicroPython::ExecutionEnvironment {
class ConsoleController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public TextFieldDelegate, public Shared::InputEventHandlerDelegate, public MicroPython::ExecutionEnvironment {
public:
ConsoleController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore
#if EPSILON_GETOPT
@@ -52,7 +54,7 @@ public:
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
// SelectableTableViewDelegate
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
void tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
// TextFieldDelegate
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
@@ -60,6 +62,9 @@ public:
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
bool textFieldDidAbortEditing(TextField * textField) override;
// InputEventHandlerDelegate
VariableBoxController * variableBoxForInputEventHandler(InputEventHandler * textInput) override;
// MicroPython::ExecutionEnvironment
ViewController * sandbox() override { return &m_sandboxController; }
void resetSandbox() override;

View File

@@ -4,11 +4,10 @@
#include <apps/i18n.h>
#include <apps/global_preferences.h>
#include <assert.h>
#include <algorithm>
namespace Code {
static inline int minInt(int x, int y) { return x < y ? x : y; }
ConsoleEditCell::ConsoleEditCell(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * delegate) :
HighlightCell(),
Responder(parentResponder),
@@ -70,7 +69,7 @@ const char * ConsoleEditCell::shiftCurrentTextAndClear() {
char * textFieldBuffer = const_cast<char *>(m_textField.text());
char * newTextPosition = textFieldBuffer + 1;
assert(previousBufferSize > 0);
size_t copyLength = minInt(previousBufferSize - 1, strlen(textFieldBuffer));
size_t copyLength = std::min(previousBufferSize - 1, strlen(textFieldBuffer));
memmove(newTextPosition, textFieldBuffer, copyLength);
newTextPosition[copyLength] = 0;
textFieldBuffer[0] = 0;

View File

@@ -19,7 +19,7 @@ void ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::setLine(Consol
void ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(bounds(), Palette::CodeBackground);
ctx->drawString(m_line->text(), KDPointZero, GlobalPreferences::sharedGlobalPreferences()->font(), textColor(m_line), isHighlighted()? Palette::Select : KDColorWhite);
ctx->drawString(m_line->text(), KDPointZero, GlobalPreferences::sharedGlobalPreferences()->font(), textColor(m_line), isHighlighted()? Palette::Select : Palette::BackgroundApps);
}
KDSize ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::minimalSizeForOptimalDisplay() const {

View File

@@ -1,10 +1,9 @@
#include "console_store.h"
#include <string.h>
#include <algorithm>
namespace Code {
static inline int minInt(int x, int y) { return x < y ? x : y; }
void ConsoleStore::startNewSession() {
if (k_historySize < 1) {
return;
@@ -12,7 +11,7 @@ void ConsoleStore::startNewSession() {
m_history[0] = makePrevious(m_history[0]);
for (int i = 0; i < k_historySize - 1; i++) {
for (size_t i = 0; i < k_historySize - 1; i++) {
if (m_history[i] == 0) {
if (m_history[i+1] == 0) {
return ;
@@ -25,7 +24,7 @@ void ConsoleStore::startNewSession() {
ConsoleLine ConsoleStore::lineAtIndex(int i) const {
assert(i >= 0 && i < numberOfLines());
int currentLineIndex = 0;
for (int j=0; j<k_historySize; j++) {
for (size_t j=0; j<k_historySize; j++) {
if (m_history[j] == 0) {
currentLineIndex++;
j++;
@@ -43,7 +42,7 @@ int ConsoleStore::numberOfLines() const {
return 0;
}
int result = 0;
for (int i = 0; i < k_historySize - 1; i++) {
for (size_t i = 0; i < k_historySize - 1; i++) {
if (m_history[i] == 0) {
result++;
if (m_history[i+1] == 0) {
@@ -96,14 +95,14 @@ const char * ConsoleStore::push(const char marker, const char * text) {
if (ConsoleLine::sizeOfConsoleLine(textLength) > k_historySize - 1) {
textLength = k_historySize - 1 - 1 - 1; // Marker, null termination and null marker.
}
int i = indexOfNullMarker();
size_t i = indexOfNullMarker();
// If needed, make room for the text we want to push.
while (i + ConsoleLine::sizeOfConsoleLine(textLength) > k_historySize - 1) {
deleteFirstLine();
i = indexOfNullMarker();
}
m_history[i] = marker;
strlcpy(&m_history[i+1], text, minInt(k_historySize-(i+1),textLength+1));
strlcpy(&m_history[i+1], text, std::min(k_historySize-(i+1),textLength+1));
m_history[i+1+textLength+1] = 0;
return &m_history[i+1];
}
@@ -113,11 +112,11 @@ ConsoleLine::Type ConsoleStore::lineTypeForMarker(char marker) const {
return static_cast<ConsoleLine::Type>(marker-1);
}
int ConsoleStore::indexOfNullMarker() const {
size_t ConsoleStore::indexOfNullMarker() const {
if (m_history[0] == 0) {
return 0;
}
for (int i=0; i<k_historySize; i++) {
for (size_t i=0; i<k_historySize; i++) {
if (m_history[i] == 0 && m_history[i+1] == 0) {
return (i+1);
}
@@ -129,13 +128,13 @@ int ConsoleStore::indexOfNullMarker() const {
void ConsoleStore::deleteLineAtIndex(int index) {
assert(index >=0 && index < numberOfLines());
int currentLineIndex = 0;
for (int i = 0; i < k_historySize - 1; i++) {
for (size_t i = 0; i < k_historySize - 1; i++) {
if (m_history[i] == 0) {
currentLineIndex++;
continue;
}
if (currentLineIndex == index) {
int nextLineStart = i;
size_t nextLineStart = i;
while (m_history[nextLineStart] != 0 && nextLineStart < k_historySize - 2) {
nextLineStart++;
}
@@ -158,7 +157,7 @@ void ConsoleStore::deleteFirstLine() {
secondLineMarkerIndex++;
}
secondLineMarkerIndex++;
for (int i=0; i<k_historySize - secondLineMarkerIndex; i++) {
for (size_t i=0; i<k_historySize - secondLineMarkerIndex; i++) {
m_history[i] = m_history[secondLineMarkerIndex+i];
}
}
@@ -174,7 +173,7 @@ void ConsoleStore::deleteLastLine() {
}
int currentLineIndex = 1;
int lastLineMarkerIndex = 0;
for (int i=0; i<k_historySize; i++) {
for (size_t i=0; i<k_historySize; i++) {
if (m_history[i] == 0) {
currentLineIndex++;
if (currentLineIndex == lineCount) {

View File

@@ -23,7 +23,7 @@ private:
static constexpr char CurrentSessionResultMarker = 0x02;
static constexpr char PreviousSessionCommandMarker = 0x03;
static constexpr char PreviousSessionResultMarker = 0x04;
static constexpr int k_historySize = 1024;
static constexpr size_t k_historySize = 1024;
static char makePrevious(char marker) {
if (marker == CurrentSessionCommandMarker || marker == CurrentSessionResultMarker) {
return marker + 0x02;
@@ -32,7 +32,7 @@ private:
}
const char * push(const char marker, const char * text);
ConsoleLine::Type lineTypeForMarker(char marker) const;
int indexOfNullMarker() const;
size_t indexOfNullMarker() const;
void deleteLineAtIndex(int index);
void deleteFirstLine();
/* When there is no room left to store a new ConsoleLine, we have to delete

View File

@@ -13,13 +13,15 @@ EditorController::EditorController(MenuController * menuController, App * python
ViewController(nullptr),
m_editorView(this, pythonDelegate),
m_script(Ion::Storage::Record()),
m_scriptIndex(-1),
m_menuController(menuController)
{
m_editorView.setTextAreaDelegates(this, this);
}
void EditorController::setScript(Script script) {
void EditorController::setScript(Script script, int scriptIndex) {
m_script = script;
m_scriptIndex = scriptIndex;
/* We edit the script direclty in the storage buffer. We thus put all the
* storage available space at the end of the current edited script and we set
@@ -35,7 +37,7 @@ void EditorController::setScript(Script script) {
* */
size_t newScriptSize = Ion::Storage::sharedStorage()->putAvailableSpaceAtEndOfRecord(m_script);
m_editorView.setText(const_cast<char *>(m_script.scriptContent()), newScriptSize - Script::k_importationStatusSize);
m_editorView.setText(const_cast<char *>(m_script.content()), newScriptSize - Script::StatusSize());
}
void EditorController::willExitApp() {
@@ -78,14 +80,6 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events:
return true;
}
if(event.hasText()){
if(event.text() == "%" && Ion::Events::isLockActive() ){
return textArea->removePreviousGlyph();
} else {
return textArea->handleEventWithText(event.text(), true, false);
}
}
if (event == Ion::Events::Backspace && textArea->selectionIsEmpty()) {
/* If the cursor is on the left of the text of a line, backspace one
@@ -131,7 +125,24 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events:
VariableBoxController * EditorController::variableBoxForInputEventHandler(InputEventHandler * textInput) {
VariableBoxController * varBox = App::app()->variableBoxController();
varBox->loadFunctionsAndVariables();
/* If the editor should be autocompleting an identifier, the variable box has
* already been loaded. We check shouldAutocomplete and not isAutocompleting,
* because the autocompletion result might be empty. */
const char * beginningOfAutocompletion = nullptr;
const char * cursor = nullptr;
PythonTextArea::AutocompletionType autocompType = m_editorView.autocompletionType(&beginningOfAutocompletion, &cursor);
if (autocompType == PythonTextArea::AutocompletionType::NoIdentifier) {
varBox->loadFunctionsAndVariables(m_scriptIndex, nullptr, 0);
} else if (autocompType == PythonTextArea::AutocompletionType::MiddleOfIdentifier) {
varBox->empty();
} else {
assert(autocompType == PythonTextArea::AutocompletionType::EndOfIdentifier);
assert(beginningOfAutocompletion != nullptr && cursor != nullptr);
assert(cursor > beginningOfAutocompletion);
varBox->loadFunctionsAndVariables(m_scriptIndex, beginningOfAutocompletion, cursor - beginningOfAutocompletion);
}
varBox->setTitle(I18n::Message::Autocomplete);
varBox->setDisplaySubtitles(true);
return varBox;
}
@@ -146,7 +157,7 @@ void EditorController::cleanStorageEmptySpace() {
Ion::Storage::Record::Data scriptValue = m_script.value();
Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(
m_script,
scriptValue.size - Script::k_importationStatusSize - (strlen(m_script.scriptContent()) + 1)); // TODO optimize number of script fetches
scriptValue.size - Script::StatusSize() - (strlen(m_script.content()) + 1)); // TODO optimize number of script fetches
}

View File

@@ -16,7 +16,8 @@ class App;
class EditorController : public ViewController, public TextAreaDelegate, public Shared::InputEventHandlerDelegate {
public:
EditorController(MenuController * menuController, App * pythonDelegate);
void setScript(Script script);
void setScript(Script script, int scriptIndex);
int scriptIndex() const { return m_scriptIndex; }
void willExitApp();
/* ViewController */
@@ -39,6 +40,7 @@ private:
StackViewController * stackController();
EditorView m_editorView;
Script m_script;
int m_scriptIndex;
MenuController * m_menuController;
};

View File

@@ -17,6 +17,10 @@ EditorView::EditorView(Responder * parentResponder, App * pythonDelegate) :
m_textArea.setScrollViewDelegate(this);
}
bool EditorView::isAutocompleting() const {
return m_textArea.isAutocompleting();
}
void EditorView::resetSelection() {
m_textArea.resetSelection();
}
@@ -53,8 +57,8 @@ void EditorView::layoutSubviews(bool force) {
/* EditorView::GutterView */
void EditorView::GutterView::drawRect(KDContext * ctx, KDRect rect) const {
KDColor textColor = KDColor::RGB24(0x919EA4);
KDColor backgroundColor = KDColor::RGB24(0xE4E6E7);
KDColor textColor = Palette::PrimaryText;
KDColor backgroundColor = Palette::CodeGutterViewBackground;
ctx->fillRect(rect, backgroundColor);

View File

@@ -9,6 +9,8 @@ namespace Code {
class EditorView : public Responder, public View, public ScrollViewDelegate {
public:
EditorView(Responder * parentResponder, App * pythonDelegate);
PythonTextArea::AutocompletionType autocompletionType(const char ** autocompletionBeginning, const char ** autocompletionEnd) const { return m_textArea.autocompletionType(nullptr, autocompletionBeginning, autocompletionEnd); }
bool isAutocompleting() const;
void resetSelection();
void setTextAreaDelegates(InputEventHandlerDelegate * inputEventHandlerDelegate, TextAreaDelegate * delegate) {
m_textArea.setDelegates(inputEventHandlerDelegate, delegate);
@@ -17,6 +19,9 @@ public:
void setText(char * textBuffer, size_t textBufferSize) {
m_textArea.setText(textBuffer, textBufferSize);
}
const char * cursorLocation() {
return m_textArea.cursorLocation();
}
bool setCursorLocation(const char * location) {
return m_textArea.setCursorLocation(location);
}

View File

@@ -385,7 +385,7 @@ void MenuController::configureScript() {
void MenuController::editScriptAtIndex(int scriptIndex) {
assert(scriptIndex >=0 && scriptIndex < m_scriptStore->numberOfScripts());
Script script = m_scriptStore->scriptAtIndex(scriptIndex);
m_editorController.setScript(script);
m_editorController.setScript(script, scriptIndex);
stackViewController()->push(&m_editorController);
}

View File

@@ -25,6 +25,7 @@ public:
void openConsoleWithScript(Script script);
void scriptContentEditionDidFinish();
void willExitApp();
int editedScriptIndex() const { return m_editorController.scriptIndex(); }
/* ViewController */
View * view() override { return &m_selectableTableView; }

View File

@@ -9,6 +9,7 @@ extern "C" {
#include "py/lexer.h"
}
#include <stdlib.h>
#include <algorithm>
namespace Code {
@@ -20,8 +21,7 @@ constexpr KDColor OperatorColor = Palette::CodeOperator;
constexpr KDColor StringColor = Palette::CodeString;
constexpr KDColor BackgroundColor = Palette::CodeBackground;
constexpr KDColor HighlightColor = Palette::CodeBackgroundSelected;
static inline const char * minPointer(const char * x, const char * y) { return x < y ? x : y; }
constexpr KDColor AutocompleteColor = KDColor::RGB24(0xC6C6C6); // TODO Palette change
static inline KDColor TokenColor(mp_token_kind_t tokenKind) {
if (tokenKind == MP_TOKEN_STRING) {
@@ -30,13 +30,96 @@ static inline KDColor TokenColor(mp_token_kind_t tokenKind) {
if (tokenKind == MP_TOKEN_INTEGER || tokenKind == MP_TOKEN_FLOAT_OR_IMAG) {
return NumberColor;
}
static_assert(MP_TOKEN_ELLIPSIS + 1 == MP_TOKEN_KW_FALSE
&& MP_TOKEN_KW_FALSE + 1 == MP_TOKEN_KW_NONE
&& MP_TOKEN_KW_NONE + 1 == MP_TOKEN_KW_TRUE
&& MP_TOKEN_KW_TRUE + 1 == MP_TOKEN_KW___DEBUG__
&& MP_TOKEN_KW___DEBUG__ + 1 == MP_TOKEN_KW_AND
&& MP_TOKEN_KW_AND + 1 == MP_TOKEN_KW_AS
&& MP_TOKEN_KW_AS + 1 == MP_TOKEN_KW_ASSERT
/* Here there are keywords that depend on MICROPY_PY_ASYNC_AWAIT, we do
* not test them */
&& MP_TOKEN_KW_BREAK + 1 == MP_TOKEN_KW_CLASS
&& MP_TOKEN_KW_CLASS + 1 == MP_TOKEN_KW_CONTINUE
&& MP_TOKEN_KW_CONTINUE + 1 == MP_TOKEN_KW_DEF
&& MP_TOKEN_KW_DEF + 1 == MP_TOKEN_KW_DEL
&& MP_TOKEN_KW_DEL + 1 == MP_TOKEN_KW_ELIF
&& MP_TOKEN_KW_ELIF + 1 == MP_TOKEN_KW_ELSE
&& MP_TOKEN_KW_ELSE + 1 == MP_TOKEN_KW_EXCEPT
&& MP_TOKEN_KW_EXCEPT + 1 == MP_TOKEN_KW_FINALLY
&& MP_TOKEN_KW_FINALLY + 1 == MP_TOKEN_KW_FOR
&& MP_TOKEN_KW_FOR + 1 == MP_TOKEN_KW_FROM
&& MP_TOKEN_KW_FROM + 1 == MP_TOKEN_KW_GLOBAL
&& MP_TOKEN_KW_GLOBAL + 1 == MP_TOKEN_KW_IF
&& MP_TOKEN_KW_IF + 1 == MP_TOKEN_KW_IMPORT
&& MP_TOKEN_KW_IMPORT + 1 == MP_TOKEN_KW_IN
&& MP_TOKEN_KW_IN + 1 == MP_TOKEN_KW_IS
&& MP_TOKEN_KW_IS + 1 == MP_TOKEN_KW_LAMBDA
&& MP_TOKEN_KW_LAMBDA + 1 == MP_TOKEN_KW_NONLOCAL
&& MP_TOKEN_KW_NONLOCAL + 1 == MP_TOKEN_KW_NOT
&& MP_TOKEN_KW_NOT + 1 == MP_TOKEN_KW_OR
&& MP_TOKEN_KW_OR + 1 == MP_TOKEN_KW_PASS
&& MP_TOKEN_KW_PASS + 1 == MP_TOKEN_KW_RAISE
&& MP_TOKEN_KW_RAISE + 1 == MP_TOKEN_KW_RETURN
&& MP_TOKEN_KW_RETURN + 1 == MP_TOKEN_KW_TRY
&& MP_TOKEN_KW_TRY + 1 == MP_TOKEN_KW_WHILE
&& MP_TOKEN_KW_WHILE + 1 == MP_TOKEN_KW_WITH
&& MP_TOKEN_KW_WITH + 1 == MP_TOKEN_KW_YIELD
&& MP_TOKEN_KW_YIELD + 1 == MP_TOKEN_OP_TILDE,
"MP_TOKEN order changed, so Code::PythonTextArea::TokenColor might need to change too.");
if (tokenKind >= MP_TOKEN_KW_FALSE && tokenKind <= MP_TOKEN_KW_YIELD) {
return KeywordColor;
}
if (tokenKind >= MP_TOKEN_OP_PLUS && tokenKind <= MP_TOKEN_OP_NOT_EQUAL) {
return OperatorColor;
}
if (tokenKind >= MP_TOKEN_DEL_EQUAL && tokenKind <= MP_TOKEN_DEL_MINUS_MORE) {
static_assert(MP_TOKEN_OP_TILDE + 1 == MP_TOKEN_OP_LESS
&& MP_TOKEN_OP_LESS + 1 == MP_TOKEN_OP_MORE
&& MP_TOKEN_OP_MORE + 1 == MP_TOKEN_OP_DBL_EQUAL
&& MP_TOKEN_OP_DBL_EQUAL + 1 == MP_TOKEN_OP_LESS_EQUAL
&& MP_TOKEN_OP_LESS_EQUAL + 1 == MP_TOKEN_OP_MORE_EQUAL
&& MP_TOKEN_OP_MORE_EQUAL + 1 == MP_TOKEN_OP_NOT_EQUAL
&& MP_TOKEN_OP_NOT_EQUAL + 1 == MP_TOKEN_OP_PIPE
&& MP_TOKEN_OP_PIPE + 1 == MP_TOKEN_OP_CARET
&& MP_TOKEN_OP_CARET + 1 == MP_TOKEN_OP_AMPERSAND
&& MP_TOKEN_OP_AMPERSAND + 1 == MP_TOKEN_OP_DBL_LESS
&& MP_TOKEN_OP_DBL_LESS + 1 == MP_TOKEN_OP_DBL_MORE
&& MP_TOKEN_OP_DBL_MORE + 1 == MP_TOKEN_OP_PLUS
&& MP_TOKEN_OP_PLUS + 1 == MP_TOKEN_OP_MINUS
&& MP_TOKEN_OP_MINUS + 1 == MP_TOKEN_OP_STAR
&& MP_TOKEN_OP_STAR + 1 == MP_TOKEN_OP_AT
&& MP_TOKEN_OP_AT + 1 == MP_TOKEN_OP_DBL_SLASH
&& MP_TOKEN_OP_DBL_SLASH + 1 == MP_TOKEN_OP_SLASH
&& MP_TOKEN_OP_SLASH + 1 == MP_TOKEN_OP_PERCENT
&& MP_TOKEN_OP_PERCENT + 1 == MP_TOKEN_OP_DBL_STAR
&& MP_TOKEN_OP_DBL_STAR + 1 == MP_TOKEN_DEL_PIPE_EQUAL
&& MP_TOKEN_DEL_PIPE_EQUAL + 1 == MP_TOKEN_DEL_CARET_EQUAL
&& MP_TOKEN_DEL_CARET_EQUAL + 1 == MP_TOKEN_DEL_AMPERSAND_EQUAL
&& MP_TOKEN_DEL_AMPERSAND_EQUAL + 1 == MP_TOKEN_DEL_DBL_LESS_EQUAL
&& MP_TOKEN_DEL_DBL_LESS_EQUAL + 1 == MP_TOKEN_DEL_DBL_MORE_EQUAL
&& MP_TOKEN_DEL_DBL_MORE_EQUAL + 1 == MP_TOKEN_DEL_PLUS_EQUAL
&& MP_TOKEN_DEL_PLUS_EQUAL + 1 == MP_TOKEN_DEL_MINUS_EQUAL
&& MP_TOKEN_DEL_MINUS_EQUAL + 1 == MP_TOKEN_DEL_STAR_EQUAL
&& MP_TOKEN_DEL_STAR_EQUAL + 1 == MP_TOKEN_DEL_AT_EQUAL
&& MP_TOKEN_DEL_AT_EQUAL + 1 == MP_TOKEN_DEL_DBL_SLASH_EQUAL
&& MP_TOKEN_DEL_DBL_SLASH_EQUAL + 1 == MP_TOKEN_DEL_SLASH_EQUAL
&& MP_TOKEN_DEL_SLASH_EQUAL + 1 == MP_TOKEN_DEL_PERCENT_EQUAL
&& MP_TOKEN_DEL_PERCENT_EQUAL + 1 == MP_TOKEN_DEL_DBL_STAR_EQUAL
&& MP_TOKEN_DEL_DBL_STAR_EQUAL + 1 == MP_TOKEN_DEL_PAREN_OPEN
&& MP_TOKEN_DEL_PAREN_OPEN + 1 == MP_TOKEN_DEL_PAREN_CLOSE
&& MP_TOKEN_DEL_PAREN_CLOSE + 1 == MP_TOKEN_DEL_BRACKET_OPEN
&& MP_TOKEN_DEL_BRACKET_OPEN + 1 == MP_TOKEN_DEL_BRACKET_CLOSE
&& MP_TOKEN_DEL_BRACKET_CLOSE + 1 == MP_TOKEN_DEL_BRACE_OPEN
&& MP_TOKEN_DEL_BRACE_OPEN + 1 == MP_TOKEN_DEL_BRACE_CLOSE
&& MP_TOKEN_DEL_BRACE_CLOSE + 1 == MP_TOKEN_DEL_COMMA
&& MP_TOKEN_DEL_COMMA + 1 == MP_TOKEN_DEL_COLON
&& MP_TOKEN_DEL_COLON + 1 == MP_TOKEN_DEL_PERIOD
&& MP_TOKEN_DEL_PERIOD + 1 == MP_TOKEN_DEL_SEMICOLON
&& MP_TOKEN_DEL_SEMICOLON + 1 == MP_TOKEN_DEL_EQUAL
&& MP_TOKEN_DEL_EQUAL + 1 == MP_TOKEN_DEL_MINUS_MORE,
"MP_TOKEN order changed, so Code::PythonTextArea::TokenColor might need to change too.");
if ((tokenKind >= MP_TOKEN_OP_TILDE && tokenKind <= MP_TOKEN_DEL_DBL_STAR_EQUAL)
|| tokenKind == MP_TOKEN_DEL_EQUAL
|| tokenKind == MP_TOKEN_DEL_MINUS_MORE)
{
return OperatorColor;
}
return Palette::CodeText;
@@ -53,6 +136,76 @@ static inline size_t TokenLength(mp_lexer_t * lex, const char * tokenPosition) {
return lex->column - lex->tok_column;
}
PythonTextArea::AutocompletionType PythonTextArea::autocompletionType(const char * autocompletionLocation, const char ** autocompletionLocationBeginning, const char ** autocompletionLocationEnd) const {
const char * location = autocompletionLocation != nullptr ? autocompletionLocation : cursorLocation();
const char * beginningOfToken = nullptr;
/* If there is already autocompleting, the cursor must be at the end of an
* identifier. Trying to compute autocompletionType will fail: because of the
* autocompletion text, the cursor seems to be in the middle of an identifier. */
AutocompletionType autocompleteType = isAutocompleting() ? AutocompletionType::EndOfIdentifier : AutocompletionType::NoIdentifier;
if (autocompletionLocationBeginning == nullptr && autocompletionLocationEnd == nullptr) {
return autocompleteType;
}
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
const char * firstNonSpace = UTF8Helper::BeginningOfWord(m_contentView.editedText(), location);
mp_lexer_t * lex = mp_lexer_new_from_str_len(0, firstNonSpace, UTF8Helper::EndOfWord(location) - firstNonSpace, 0);
const char * tokenStart;
const char * tokenEnd;
_mp_token_kind_t currentTokenKind = lex->tok_kind;
while (currentTokenKind != MP_TOKEN_NEWLINE && currentTokenKind != MP_TOKEN_END) {
tokenStart = firstNonSpace + lex->tok_column - 1;
tokenEnd = tokenStart + TokenLength(lex, tokenStart);
if (location < tokenStart) {
// The location for autocompletion is not in an identifier
assert(autocompleteType == AutocompletionType::NoIdentifier);
break;
}
if (location <= tokenEnd) {
if (currentTokenKind == MP_TOKEN_NAME
|| (currentTokenKind >= MP_TOKEN_KW_FALSE
&& currentTokenKind <= MP_TOKEN_KW_YIELD))
{
/* The location for autocompletion is in the middle or at the end of
* an identifier. */
beginningOfToken = tokenStart;
/* If autocompleteType is already EndOfIdentifier, we are
* autocompleting, so we do not need to update autocompleteType. If we
* recomputed autocompleteType now, we might wrongly think that it is
* MiddleOfIdentifier because of the autocompetion text.
* Example : fin|ally -> the lexer is at the end of "fin", but because
* we are autocompleting with "ally", the lexer thinks the cursor is
* in the middle of an identifier. */
if (autocompleteType != AutocompletionType::EndOfIdentifier) {
autocompleteType = location < tokenEnd ? AutocompletionType::MiddleOfIdentifier : AutocompletionType::EndOfIdentifier;
}
}
break;
}
mp_lexer_to_next(lex);
currentTokenKind = lex->tok_kind;
}
mp_lexer_free(lex);
nlr_pop();
}
if (autocompletionLocationBeginning != nullptr) {
*autocompletionLocationBeginning = beginningOfToken;
}
if (autocompletionLocationEnd != nullptr) {
*autocompletionLocationEnd = location;
}
assert(!isAutocompleting() || autocompleteType == AutocompletionType::EndOfIdentifier);
return autocompleteType;
}
const char * PythonTextArea::ContentView::textToAutocomplete() const {
return UTF8Helper::BeginningOfWord(editedText(), cursorLocation());
}
void PythonTextArea::ContentView::loadSyntaxHighlighter() {
m_pythonDelegate->initPythonWithUser(this);
}
@@ -76,23 +229,7 @@ void PythonTextArea::ContentView::clearRect(KDContext * ctx, KDRect rect) const
void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t byteLength, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const {
LOG_DRAW("Drawing \"%.*s\"\n", byteLength, text);
if (!m_pythonDelegate->isPythonUser(this)) {
const char * lineStart = UTF8Helper::CodePointAtGlyphOffset(text, fromColumn);
const char * lineEnd = UTF8Helper::CodePointAtGlyphOffset(text, toColumn);
drawStringAt(
ctx,
line,
fromColumn,
lineStart,
minPointer(text + byteLength, lineEnd) - lineStart,
StringColor,
BackgroundColor,
selectionStart,
selectionEnd,
HighlightColor
);
return;
}
assert(m_pythonDelegate->isPythonUser(this));
/* We're using the MicroPython lexer to do syntax highlighting on a per-line
* basis. This can work, however the MicroPython lexer won't accept a line
@@ -107,7 +244,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
line,
fromColumn,
spacesStart,
minPointer(text + byteLength, firstNonSpace) - spacesStart,
std::min(text + byteLength, firstNonSpace) - spacesStart,
StringColor,
BackgroundColor,
selectionStart,
@@ -118,6 +255,8 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
return;
}
const char * autocompleteStart = m_autocomplete ? m_cursorLocation : nullptr;
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_lexer_t * lex = mp_lexer_new_from_str_len(0, firstNonSpace, byteLength - (firstNonSpace - text), 0);
@@ -135,7 +274,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
line,
UTF8Helper::GlyphOffsetAtCodePoint(text, tokenEnd),
tokenEnd,
minPointer(text + byteLength, tokenFrom) - tokenEnd,
std::min(text + byteLength, tokenFrom) - tokenEnd,
StringColor,
BackgroundColor,
selectionStart,
@@ -144,12 +283,16 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
}
tokenLength = TokenLength(lex, tokenFrom);
tokenEnd = tokenFrom + tokenLength;
// If the token is being autocompleted, use DefaultColor
KDColor color = (tokenFrom <= autocompleteStart && autocompleteStart < tokenEnd) ? Palette::CodeText : TokenColor(lex->tok_kind);
LOG_DRAW("Draw \"%.*s\" for token %d\n", tokenLength, tokenFrom, lex->tok_kind);
drawStringAt(ctx, line,
UTF8Helper::GlyphOffsetAtCodePoint(text, tokenFrom),
tokenFrom,
tokenLength,
TokenColor(lex->tok_kind),
color,
BackgroundColor,
selectionStart,
selectionEnd,
@@ -161,6 +304,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
tokenFrom += tokenLength;
// Even if the token is being autocompleted, use CommentColor
if (tokenFrom < text + byteLength) {
LOG_DRAW("Draw comment \"%.*s\" from %d\n", byteLength - (tokenFrom - text), firstNonSpace, tokenFrom);
drawStringAt(ctx, line,
@@ -177,6 +321,22 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
mp_lexer_free(lex);
nlr_pop();
}
// Redraw the autocompleted word in the right color
if (m_autocomplete && autocompleteStart >= text && autocompleteStart < text + byteLength) {
assert(m_autocompletionEnd != nullptr && m_autocompletionEnd > autocompleteStart);
drawStringAt(
ctx,
line,
UTF8Helper::GlyphOffsetAtCodePoint(text, autocompleteStart),
autocompleteStart,
std::min(text + byteLength, m_autocompletionEnd) - autocompleteStart,
AutocompleteColor,
BackgroundColor,
nullptr,
nullptr,
HighlightColor);
}
}
KDRect PythonTextArea::ContentView::dirtyRectFromPosition(const char * position, bool includeFollowingLines) const {
@@ -194,4 +354,158 @@ KDRect PythonTextArea::ContentView::dirtyRectFromPosition(const char * position,
);
}
bool PythonTextArea::handleEvent(Ion::Events::Event event) {
if (m_contentView.isAutocompleting()) {
// Handle event with autocompletion
if (event == Ion::Events::Right
|| event == Ion::Events::ShiftRight
|| event == Ion::Events::OK)
{
m_contentView.reloadRectFromPosition(m_contentView.cursorLocation(), false);
acceptAutocompletion(event != Ion::Events::ShiftRight);
if (event != Ion::Events::ShiftRight) {
// Do not process the event more
scrollToCursor();
return true;
}
} else if (event == Ion::Events::Toolbox
|| event == Ion::Events::Var
|| event == Ion::Events::Shift
|| event == Ion::Events::Alpha
|| event == Ion::Events::OnOff)
{
} else if(event == Ion::Events::Up
|| event == Ion::Events::Down)
{
cycleAutocompletion(event == Ion::Events::Down);
return true;
} else {
removeAutocompletion();
m_contentView.reloadRectFromPosition(m_contentView.cursorLocation(), false);
if (event == Ion::Events::Back) {
// Do not process the event more
return true;
}
}
}
bool result = TextArea::handleEvent(event);
if (event == Ion::Events::Backspace && !m_contentView.isAutocompleting() && selectionIsEmpty()) {
/* We want to add autocompletion when we are editing a word (after adding or
* deleting text). So if nothing is selected, we add the autocompletion if
* the event is backspace, as autocompletion has already been added if the
* event added text, in handleEventWithText. */
addAutocompletion();
}
return result;
}
bool PythonTextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) {
if (*text == 0) {
return false;
}
if (m_contentView.isAutocompleting()) {
removeAutocompletion();
}
bool result = TextArea::handleEventWithText(text, indentation, forceCursorRightOfText);
addAutocompletion();
return result;
}
void PythonTextArea::removeAutocompletion() {
assert(m_contentView.isAutocompleting());
removeAutocompletionText();
m_contentView.setAutocompleting(false);
}
void PythonTextArea::removeAutocompletionText() {
assert(m_contentView.isAutocompleting());
assert(m_contentView.autocompletionEnd() != nullptr);
const char * autocompleteStart = m_contentView.cursorLocation();
const char * autocompleteEnd = m_contentView.autocompletionEnd();
assert(autocompleteEnd != nullptr && autocompleteEnd > autocompleteStart);
m_contentView.removeText(autocompleteStart, autocompleteEnd);
}
void PythonTextArea::addAutocompletion() {
assert(!m_contentView.isAutocompleting());
const char * autocompletionTokenBeginning = nullptr;
const char * autocompletionLocation = const_cast<char *>(cursorLocation());
m_autocompletionResultIndex = 0;
if (autocompletionType(autocompletionLocation, &autocompletionTokenBeginning) != AutocompletionType::EndOfIdentifier) {
// The cursor is not at the end of an identifier.
return;
}
// First load variables and functions that complete the textToAutocomplete
const int scriptIndex = m_contentView.pythonDelegate()->menuController()->editedScriptIndex();
m_contentView.pythonDelegate()->variableBoxController()->loadFunctionsAndVariables(scriptIndex, autocompletionTokenBeginning, autocompletionLocation - autocompletionTokenBeginning);
if (addAutocompletionTextAtIndex(0)) {
m_contentView.setAutocompleting(true);
}
}
bool PythonTextArea::addAutocompletionTextAtIndex(int nextIndex, int * currentIndexToUpdate) {
// The variable box should be loaded at this point
const char * autocompletionTokenBeginning = nullptr;
const char * autocompletionLocation = const_cast<char *>(cursorLocation());
AutocompletionType type = autocompletionType(autocompletionLocation, &autocompletionTokenBeginning); // Done to get autocompletionTokenBeginning
assert(type == AutocompletionType::EndOfIdentifier);
(void)type; // Silence warnings
VariableBoxController * varBox = m_contentView.pythonDelegate()->variableBoxController();
int textToInsertLength = 0;
bool addParentheses = false;
const char * textToInsert = varBox->autocompletionAlternativeAtIndex(autocompletionLocation - autocompletionTokenBeginning, &textToInsertLength, &addParentheses, nextIndex, currentIndexToUpdate);
if (textToInsert == nullptr) {
return false;
}
if (textToInsertLength > 0) {
// Try to insert the text (this might fail if the buffer is full)
if (!m_contentView.insertTextAtLocation(textToInsert, const_cast<char *>(autocompletionLocation), textToInsertLength)) {
return false;
}
autocompletionLocation += textToInsertLength;
m_contentView.setAutocompletionEnd(autocompletionLocation);
}
// Try to insert the parentheses if needed
const char * parentheses = ScriptNodeCell::k_parentheses;
constexpr int parenthesesLength = 2;
assert(strlen(parentheses) == parenthesesLength);
/* If couldInsertText is false, we should not try to add the parentheses as
* there was already not enough space to add the autocompletion. */
if (addParentheses && m_contentView.insertTextAtLocation(parentheses, const_cast<char *>(autocompletionLocation), parenthesesLength)) {
m_contentView.setAutocompleting(true);
m_contentView.setAutocompletionEnd(autocompletionLocation + parenthesesLength);
}
return true;
}
void PythonTextArea::cycleAutocompletion(bool downwards) {
assert(m_contentView.isAutocompleting());
removeAutocompletionText();
addAutocompletionTextAtIndex(m_autocompletionResultIndex + (downwards ? 1 : -1), &m_autocompletionResultIndex);
}
void PythonTextArea::acceptAutocompletion(bool moveCursorToEndOfAutocompletion) {
assert(m_contentView.isAutocompleting());
// Save the cursor location
const char * previousCursorLocation = cursorLocation();
removeAutocompletion();
m_contentView.pythonDelegate()->variableBoxController()->setSender(this);
m_contentView.pythonDelegate()->variableBoxController()->insertAutocompletionResultAtIndex(m_autocompletionResultIndex);
// insertAutocompletionResultAtIndex already added the autocompletion
// If we did not want to move the cursor, restore its position.
if (!moveCursorToEndOfAutocompletion) {
setCursorLocation(previousCursorLocation);
}
}
}

View File

@@ -9,21 +9,46 @@ class App;
class PythonTextArea : public TextArea {
public:
enum class AutocompletionType : uint8_t {
EndOfIdentifier,
MiddleOfIdentifier,
NoIdentifier
};
PythonTextArea(Responder * parentResponder, App * pythonDelegate, const KDFont * font) :
TextArea(parentResponder, &m_contentView, font),
m_contentView(pythonDelegate, font)
m_contentView(pythonDelegate, font),
m_autocompletionResultIndex(0)
{
}
void loadSyntaxHighlighter() { m_contentView.loadSyntaxHighlighter(); }
void unloadSyntaxHighlighter() { m_contentView.unloadSyntaxHighlighter(); }
bool handleEvent(Ion::Events::Event event) override;
bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override;
/* autocompletionType returns:
* - EndOfIdentifier if there is currently autocompletion, or if the cursor is
* at the end of an identifier,
* - MiddleOfIdentifier is the cursor is in the middle of an identifier,
* - No identifier otherwise.
* The autocompletionLocation can be provided with autocompletionLocation, or
* retreived with autocompletionLocationBeginning and autocompletionLocationEnd. */
AutocompletionType autocompletionType(const char * autocompletionLocation = nullptr, const char ** autocompletionLocationBeginning = nullptr, const char ** autocompletionLocationEnd = nullptr) const;
bool isAutocompleting() const { return m_contentView.isAutocompleting(); }
protected:
class ContentView : public TextArea::ContentView {
public:
ContentView(App * pythonDelegate, const KDFont * font) :
TextArea::ContentView(font),
m_pythonDelegate(pythonDelegate)
m_pythonDelegate(pythonDelegate),
m_autocomplete(false),
m_autocompletionEnd(nullptr)
{
}
App * pythonDelegate() { return m_pythonDelegate; }
void setAutocompleting(bool autocomplete) { m_autocomplete = autocomplete; }
bool isAutocompleting() const { return m_autocomplete; }
const char * autocompletionEnd() const { assert(m_autocomplete); return m_autocompletionEnd; }
void setAutocompletionEnd(const char * end) { m_autocompletionEnd = end; }
const char * textToAutocomplete() const;
void loadSyntaxHighlighter();
void unloadSyntaxHighlighter();
void clearRect(KDContext * ctx, KDRect rect) const override;
@@ -31,10 +56,19 @@ protected:
KDRect dirtyRectFromPosition(const char * position, bool includeFollowingLines) const override;
private:
App * m_pythonDelegate;
bool m_autocomplete;
const char * m_autocompletionEnd;
};
private:
void removeAutocompletion();
void removeAutocompletionText(); // Just removes the suggested text, not the autocompletion mode
void addAutocompletion();
bool addAutocompletionTextAtIndex(int nextIndex, int * currentIndexToUpdate = nullptr); // Assumes the var box is already loaded
void cycleAutocompletion(bool downwards);
void acceptAutocompletion(bool moveCursorToEndOfAutocompletion);
const ContentView * nonEditableContentView() const override { return &m_contentView; }
ContentView m_contentView;
int m_autocompletionResultIndex;
};
}

View File

@@ -120,7 +120,18 @@ const ToolboxMessageTree MatplotlibPyplotModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPlot, I18n::Message::PythonPlot),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScatter, I18n::Message::PythonScatter),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandShow, I18n::Message::PythonShow),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandText, I18n::Message::PythonText)
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandText, I18n::Message::PythonText),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlue, I18n::Message::PythonColorBlue, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorRed, I18n::Message::PythonColorRed, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGreen, I18n::Message::PythonColorGreen, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorYellow, I18n::Message::PythonColorYellow, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBrown, I18n::Message::PythonColorBrown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlack, I18n::Message::PythonColorBlack, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorWhite, I18n::Message::PythonColorWhite, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPink, I18n::Message::PythonColorPink, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorOrange, I18n::Message::PythonColorOrange, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPurple, I18n::Message::PythonColorPurple, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGrey, I18n::Message::PythonColorGrey, false)
};
const ToolboxMessageTree TurtleModuleChildren[] = {
@@ -141,21 +152,23 @@ const ToolboxMessageTree TurtleModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPenup, I18n::Message::PythonTurtlePenup, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPensize, I18n::Message::PythonTurtlePensize),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandIsdown, I18n::Message::PythonTurtleIsdown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandWrite, I18n::Message::PythonTurtleWrite),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandReset, I18n::Message::PythonTurtleReset, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandShowturtle, I18n::Message::PythonTurtleShowturtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandHideturtle, I18n::Message::PythonTurtleHideturtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColor, I18n::Message::PythonTurtleColor, false, I18n::Message::PythonTurtleCommandColorWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlue, I18n::Message::PythonTurtleBlue, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRed, I18n::Message::PythonTurtleRed, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGreen, I18n::Message::PythonTurtleGreen, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandYellow, I18n::Message::PythonTurtleYellow, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBrown, I18n::Message::PythonTurtleBrown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlack, I18n::Message::PythonTurtleBlack, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandWhite, I18n::Message::PythonTurtleWhite, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPink, I18n::Message::PythonTurtlePink, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandOrange, I18n::Message::PythonTurtleOrange, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPurple, I18n::Message::PythonTurtlePurple, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGrey, I18n::Message::PythonTurtleGrey, false)
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColor, I18n::Message::PythonTurtleColor),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlue, I18n::Message::PythonColorBlue, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorRed, I18n::Message::PythonColorRed, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGreen, I18n::Message::PythonColorGreen, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorYellow, I18n::Message::PythonColorYellow, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBrown, I18n::Message::PythonColorBrown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlack, I18n::Message::PythonColorBlack, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorWhite, I18n::Message::PythonColorWhite, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPink, I18n::Message::PythonColorPink, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorOrange, I18n::Message::PythonColorOrange, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPurple, I18n::Message::PythonColorPurple, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGrey, I18n::Message::PythonColorGrey, false)
};
const ToolboxMessageTree RandomModuleChildren[] = {
@@ -277,14 +290,15 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBackward, I18n::Message::PythonTurtleBackward),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandBar, I18n::Message::PythonBar),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandBin, I18n::Message::PythonBin),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlack, I18n::Message::PythonTurtleBlack, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlue, I18n::Message::PythonTurtleBlue, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBrown, I18n::Message::PythonTurtleBrown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlack, I18n::Message::PythonColorBlack, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlue, I18n::Message::PythonColorBlue, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBrown, I18n::Message::PythonColorBrown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCeil, I18n::Message::PythonCeil),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandChoice, I18n::Message::PythonChoice),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandCircle, I18n::Message::PythonTurtleCircle),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCmathFunction, I18n::Message::PythonCmathFunction, false, I18n::Message::PythonCommandCmathFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandComplex, I18n::Message::PythonComplex),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCopySign, I18n::Message::PythonCopySign),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCos, I18n::Message::PythonCos),
@@ -317,8 +331,8 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetPixel, I18n::Message::PythonGetPixel),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetrandbits, I18n::Message::PythonGetrandbits),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGoto, I18n::Message::PythonTurtleGoto),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGreen, I18n::Message::PythonTurtleGreen, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGrey, I18n::Message::PythonTurtleGrey, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGreen, I18n::Message::PythonColorGreen, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGrey, I18n::Message::PythonColorGrey, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGrid, I18n::Message::PythonGrid),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandHeading, I18n::Message::PythonTurtleHeading, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandHex, I18n::Message::PythonHex),
@@ -364,19 +378,19 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandModf, I18n::Message::PythonModf),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandMonotonic, I18n::Message::PythonMonotonic, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandOct, I18n::Message::PythonOct),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandOrange, I18n::Message::PythonTurtleOrange, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorOrange, I18n::Message::PythonColorOrange, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPendown, I18n::Message::PythonTurtlePendown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPenup, I18n::Message::PythonTurtlePenup, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPensize, I18n::Message::PythonTurtlePensize),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPhase, I18n::Message::PythonPhase),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantPi, I18n::Message::PythonConstantPi, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPink, I18n::Message::PythonTurtlePink, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPink, I18n::Message::PythonColorPink, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPolar, I18n::Message::PythonPolar),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPosition, I18n::Message::PythonTurtlePosition, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPower, I18n::Message::PythonPower),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPlot, I18n::Message::PythonPlot),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPrint, I18n::Message::PythonPrint),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPurple, I18n::Message::PythonTurtlePurple, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPurple, I18n::Message::PythonColorPurple, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRadians, I18n::Message::PythonRadians),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRandint, I18n::Message::PythonRandint),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRandom, I18n::Message::PythonRandom, false),
@@ -385,7 +399,7 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRangeStartStop, I18n::Message::PythonRangeStartStop),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRangeStop, I18n::Message::PythonRangeStop),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRect, I18n::Message::PythonRect),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRed, I18n::Message::PythonTurtleRed, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorRed, I18n::Message::PythonColorRed, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandReset, I18n::Message::PythonTurtleReset, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRight, I18n::Message::PythonTurtleRight),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRound, I18n::Message::PythonRound),
@@ -409,8 +423,9 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTrunc, I18n::Message::PythonTrunc),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTurtleFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandTurtleFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandUniform, I18n::Message::PythonUniform),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandWhite, I18n::Message::PythonTurtleWhite, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandYellow, I18n::Message::PythonTurtleYellow, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorWhite, I18n::Message::PythonColorWhite, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandWrite, I18n::Message::PythonTurtleWrite),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorYellow, I18n::Message::PythonColorYellow, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImag, I18n::Message::PythonImag, false, I18n::Message::PythonCommandImagWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandReal, I18n::Message::PythonReal, false, I18n::Message::PythonCommandRealWithoutArg)
};
@@ -420,11 +435,39 @@ const ToolboxMessageTree functionsChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandReturn, I18n::Message::Default)
};
const ToolboxMessageTree fileChildren[] {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileOpen, I18n::Message::PythonFileOpen, false, I18n::Message::PythonCommandFileOpenWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileClose, I18n::Message::PythonFileClose, false, I18n::Message::PythonCommandFileCloseWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileClosed, I18n::Message::PythonFileClosed, false, I18n::Message::PythonCommandFileClosedWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileMode, I18n::Message::PythonFileMode, false, I18n::Message::PythonCommandFileModeWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileName, I18n::Message::PythonFileName, false, I18n::Message::PythonCommandFileNameWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileRead, I18n::Message::PythonFileRead, false, I18n::Message::PythonCommandFileReadWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadable, I18n::Message::PythonFileReadable, false, I18n::Message::PythonCommandFileReadableWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadline, I18n::Message::PythonFileReadline, false, I18n::Message::PythonCommandFileReadlineWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadlines, I18n::Message::PythonFileReadlines, false, I18n::Message::PythonCommandFileReadlinesWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileSeek, I18n::Message::PythonFileSeek, false, I18n::Message::PythonCommandFileSeekWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileSeekable, I18n::Message::PythonFileSeekable, false, I18n::Message::PythonCommandFileSeekableWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileTell, I18n::Message::PythonFileTell, false, I18n::Message::PythonCommandFileTellWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileTruncate, I18n::Message::PythonFileTruncate, false, I18n::Message::PythonCommandFileTruncateWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWrite, I18n::Message::PythonFileWrite, false, I18n::Message::PythonCommandFileWriteWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWritable, I18n::Message::PythonFileWritable, false, I18n::Message::PythonCommandFileWritableWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWritelines, I18n::Message::PythonFileWritelines, false, I18n::Message::PythonCommandFileWritelinesWithoutArg),
};
const ToolboxMessageTree exceptionsChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::TryExcept1ErrorWithArg, I18n::Message::Default, false, I18n::Message::TryExcept1Error),
ToolboxMessageTree::Leaf(I18n::Message::TryExcept1ErrorElseWithArg, I18n::Message::Default, false, I18n::Message::TryExcept1ErrorElse),
ToolboxMessageTree::Leaf(I18n::Message::TryExcept2ErrorWithArg, I18n::Message::Default, false, I18n::Message::TryExcept2Error),
ToolboxMessageTree::Leaf(I18n::Message::WithInstructionWithArg, I18n::Message::Default, false, I18n::Message::WithInstruction),
};
const ToolboxMessageTree menu[] = {
ToolboxMessageTree::Node(I18n::Message::LoopsAndTests, loopsAndTestsChildren),
ToolboxMessageTree::Node(I18n::Message::Modules, modulesChildren),
ToolboxMessageTree::Node(I18n::Message::Catalog, catalogChildren),
ToolboxMessageTree::Node(I18n::Message::Functions, functionsChildren)
ToolboxMessageTree::Node(I18n::Message::Functions, functionsChildren),
ToolboxMessageTree::Node(I18n::Message::Files, fileChildren),
ToolboxMessageTree::Node(I18n::Message::Exceptions, exceptionsChildren)
};
const ToolboxMessageTree toolboxModel = ToolboxMessageTree::Node(I18n::Message::Toolbox, menu);
@@ -435,6 +478,20 @@ PythonToolbox::PythonToolbox() :
{
}
const ToolboxMessageTree * PythonToolbox::moduleChildren(const char * name, int * numberOfNodes) const {
for (ToolboxMessageTree t : modulesChildren) {
if (strcmp(I18n::translate(t.label()), name) == 0) {
const int childrenCount = t.numberOfChildren();
if (numberOfNodes != nullptr) {
*numberOfNodes = childrenCount;
}
assert(childrenCount > 0);
return static_cast<const ToolboxMessageTree *>(t.childAtIndex(0));
}
}
return nullptr;
}
bool PythonToolbox::handleEvent(Ion::Events::Event event) {
if (Toolbox::handleEvent(event)) {
return true;
@@ -450,7 +507,7 @@ bool PythonToolbox::handleEvent(Ion::Events::Event event) {
}
KDCoordinate PythonToolbox::rowHeight(int j) {
if (typeAtLocation(0, j) == Toolbox::LeafCellType && m_messageTreeModel->label() == I18n::Message::IfStatementMenu) {
if (typeAtLocation(0, j) == Toolbox::LeafCellType && (m_messageTreeModel->label() == I18n::Message::IfStatementMenu || m_messageTreeModel->label() == I18n::Message::Exceptions)) {
/* To get the exact height needed for each cell, we have to compute its
* text size, which means scan the text char by char to look for '\n'
* chars. This is very costly and ruins the speed performance when
@@ -460,7 +517,7 @@ KDCoordinate PythonToolbox::rowHeight(int j) {
* We thus decided to compute the real height only for the ifStatement
* children of the toolbox, which is the only menu that has special height
* rows. */
const ToolboxMessageTree * messageTree = static_cast<const ToolboxMessageTree *>(m_messageTreeModel->children(j));
const ToolboxMessageTree * messageTree = static_cast<const ToolboxMessageTree *>(m_messageTreeModel->childAtIndex(j));
return k_font->stringSize(I18n::translate(messageTree->label())).height() + 2*Metric::TableCellVerticalMargin + (messageTree->text() == I18n::Message::Default ? 0 : Toolbox::rowHeight(j));
}
return Toolbox::rowHeight(j);
@@ -468,7 +525,7 @@ KDCoordinate PythonToolbox::rowHeight(int j) {
bool PythonToolbox::selectLeaf(int selectedRow) {
m_selectableTableView.deselectTable();
ToolboxMessageTree * node = (ToolboxMessageTree *)m_messageTreeModel->children(selectedRow);
ToolboxMessageTree * node = (ToolboxMessageTree *)m_messageTreeModel->childAtIndex(selectedRow);
const char * editedText = I18n::translate(node->insertedText());
// strippedEditedText array needs to be in the same scope as editedText
char strippedEditedText[k_maxMessageSize];
@@ -509,7 +566,7 @@ void PythonToolbox::scrollToLetter(char letter) {
char lowerLetter = tolower(letter);
int index = -1;
for (int i = 0; i < m_messageTreeModel->numberOfChildren(); i++) {
char l = tolower(I18n::translate(m_messageTreeModel->children(i)->label())[0]);
char l = tolower(I18n::translate(m_messageTreeModel->childAtIndex(i)->label())[0]);
if (l == lowerLetter) {
index = i;
break;

View File

@@ -10,7 +10,11 @@ namespace Code {
class PythonToolbox : public Toolbox {
public:
// PythonToolbox
PythonToolbox();
const ToolboxMessageTree * moduleChildren(const char * name, int * numberOfNodes) const;
// Toolbox
bool handleEvent(Ion::Events::Event event) override;
const ToolboxMessageTree * rootModel() const override;
protected:

View File

@@ -65,22 +65,54 @@ bool Script::nameCompliant(const char * name) {
return false;
}
bool Script::importationStatus() const {
assert(!isNull());
Data d = value();
return (((char *)d.buffer)[0] == 1);
uint8_t * StatusFromData(Script::Data d) {
return const_cast<uint8_t *>(static_cast<const uint8_t *>(d.buffer));
}
void Script::toggleImportationStatus() {
bool Script::autoImportationStatus() const {
return getStatutBit(k_autoImportationStatusMask);
}
void Script::toggleAutoimportationStatus() {
assert(!isNull());
Data d = value();
((char *)d.buffer)[0] = (((char *)d.buffer)[0] == 1 ? 0 : 1);
*StatusFromData(d) ^= k_autoImportationStatusMask;
setValue(d);
}
const char * Script::scriptContent() const {
const char * Script::content() const {
Data d = value();
return ((const char *)d.buffer) + StatusSize();
}
bool Script::fetchedFromConsole() const {
return getStatutBit(k_fetchedFromConsoleMask);
}
void Script::setFetchedFromConsole(bool fetched) {
setStatutBit(k_fetchedFromConsoleMask, k_fetchedFromConsoleOffset, fetched);
}
bool Script::fetchedForVariableBox() const {
return getStatutBit(k_fetchedForVariableBoxMask);
}
void Script::setFetchedForVariableBox(bool fetched) {
setStatutBit(k_fetchedForVariableBoxMask, k_fetchedForVariableBoxOffset, fetched);
}
bool Script::getStatutBit(uint8_t mask) const {
assert(!isNull());
Data d = value();
return (const char *)d.buffer + k_importationStatusSize;
return ((*StatusFromData(d)) & mask) != 0;
}
void Script::setStatutBit(uint8_t mask, uint8_t offset, bool statusBit) {
assert(!isNull());
Data d = value();
uint8_t * status = StatusFromData(d);
*status = ((*status) & ~mask) | (static_cast<uint8_t>(statusBit) << offset); //TODO Create and use a bit operations library
setValue(d);
}
}

View File

@@ -5,16 +5,36 @@
namespace Code {
/* Record : | Total Size | Name | Body |
* Script: | AutoImportationStatus | Content |*/
/* Record: | Size | Name | Body |
* Script: | | | Status | Content |
*
*
* |FetchedForVariableBoxBit
* Status is one byte long: xxxxxxxx
* ^ ^
* FetchedFromConsoleBit AutoImportationBit
*
* AutoImportationBit is 1 if the script should be auto imported when the
* console opens.
*
* FetchedFromConsoleBit is 1 if its content has been fetched from the console,
* so we can retrieve the correct variables afterwards in the variable box.
*
* FetchedForVariableBoxBit is used to prevent circular importation problems,
* such as scriptA importing scriptB, which imports scriptA. Once we get the
* variables from a script to put them in the variable box, we switch the bit to
* 1 and won't reload it afterwards. */
class Script : public Ion::Storage::Record {
private:
// Default script names are chosen between script1 and script99
static constexpr int k_maxNumberOfDefaultScriptNames = 99;
static constexpr int k_defaultScriptNameNumberMaxSize = 2; // Numbers from 1 to 99 have 2 digits max
// See the comment at the beginning of the file
static constexpr size_t k_statusSize = 1;
public:
static constexpr size_t k_importationStatusSize = 1;
static constexpr int k_defaultScriptNameMaxSize = 6 + k_defaultScriptNameNumberMaxSize + 1;
/* 6 = strlen("script")
* k_defaultScriptNameNumberMaxSize = maxLength of integers between 1 and 99
@@ -22,11 +42,29 @@ public:
static bool DefaultName(char buffer[], size_t bufferSize);
static bool nameCompliant(const char * name);
static constexpr size_t StatusSize() { return k_statusSize; }
Script(Ion::Storage::Record r) : Record(r) {}
bool importationStatus() const;
void toggleImportationStatus();
const char * scriptContent() const;
Script(Ion::Storage::Record r = Ion::Storage::Record()) : Record(r) {}
bool autoImportationStatus() const;
void toggleAutoimportationStatus();
const char * content() const;
/* Fetched status */
bool fetchedFromConsole() const;
void setFetchedFromConsole(bool fetched);
bool fetchedForVariableBox() const;
void setFetchedForVariableBox(bool fetched);
private:
static constexpr uint8_t k_autoImportationStatusMask = 0b1;
static constexpr uint8_t k_fetchedForVariableBoxOffset = 7;
static constexpr uint8_t k_fetchedFromConsoleOffset = 6;
static constexpr uint8_t k_fetchedForVariableBoxMask = 0b1 << k_fetchedForVariableBoxOffset;
static constexpr uint8_t k_fetchedFromConsoleMask = 0b1 << k_fetchedFromConsoleOffset;
bool getStatutBit(uint8_t offset) const;
void setStatutBit(uint8_t mask, uint8_t offset, bool value);
};
}

View File

@@ -1,33 +1,35 @@
#ifndef CODE_SCRIPT_NODE_H
#define CODE_SCRIPT_NODE_H
#include <stddef.h>
#include <stdint.h>
namespace Code {
class ScriptNode {
public:
enum class Type {
Function = 0,
Variable = 1
enum class Type : bool {
WithoutParentheses,
WithParentheses
};
ScriptNode() :
m_type(Type::Function), m_name(nullptr), m_scriptIndex(0) {}
static ScriptNode FunctionNode(const char * name, uint16_t scriptIndex) {
return ScriptNode(Type::Function, name, scriptIndex);
}
static ScriptNode VariableNode(const char * name, uint16_t scriptIndex) {
return ScriptNode(Type::Variable, name, scriptIndex);
}
ScriptNode(Type type = Type::WithoutParentheses, const char * name = nullptr, int nameLength = -1, const char * nodeSourceName = nullptr, const char * description = nullptr) :
m_type(type),
m_name(name),
m_nodeSourceName(nodeSourceName),
m_description(description),
m_nameLength(nameLength)
{}
Type type() const { return m_type; }
const char * name() const { return m_name; }
uint16_t scriptIndex() const { return m_scriptIndex; }
int nameLength() const { return static_cast<int>(m_nameLength); }
const char * nodeSourceName() const { return m_nodeSourceName; }
const char * description() const { return m_description; }
private:
ScriptNode(Type type, const char * name, uint16_t scriptIndex) :
m_type(type), m_name(name), m_scriptIndex(scriptIndex) {}
Type m_type;
const char * m_name;
uint16_t m_scriptIndex;
const char * m_nodeSourceName;
const char * m_description;
size_t m_nameLength;
};
}

View File

@@ -7,47 +7,51 @@ namespace Code {
constexpr char ScriptNodeCell::k_parentheses[];
constexpr char ScriptNodeCell::k_parenthesesWithEmpty[];
ScriptNodeCell::ScriptNodeView::ScriptNodeView() :
HighlightCell(),
m_scriptNode(nullptr),
m_scriptStore(nullptr)
{
}
void ScriptNodeCell::ScriptNodeView::setScriptNode(ScriptNode * scriptNode) {
m_scriptNode = scriptNode;
}
void ScriptNodeCell::ScriptNodeView::setScriptStore(ScriptStore * scriptStore) {
m_scriptStore = scriptStore;
}
void ScriptNodeCell::ScriptNodeView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->drawString(m_scriptNode->name(), KDPoint(0, Metric::TableCellVerticalMargin), k_font, Palette::CodeText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
KDSize nameSize = k_font->stringSize(m_scriptNode->name());
if (m_scriptNode->type() == ScriptNode::Type::Function) {
ctx->drawString(ScriptNodeCell::k_parentheses, KDPoint(nameSize.width(), Metric::TableCellVerticalMargin), k_font, Palette::CodeText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
const KDColor backgroundColor = isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground;
// If it exists, draw the description name.
const char * descriptionName = m_scriptNode->description();
if (descriptionName != nullptr) {
ctx->drawString(descriptionName, KDPoint(0, m_frame.height() - k_bottomMargin - k_font->glyphSize().height()), k_font, Palette::GreyDark, backgroundColor);
}
// Draw the node name
const char * nodeName = m_scriptNode->name();
const int nodeNameLength = m_scriptNode->nameLength();
KDSize nameSize = k_font->stringSize(nodeName, nodeNameLength);
const KDCoordinate nodeNameY = k_topMargin;
ctx->drawString(nodeName, KDPoint(0, nodeNameY), k_font, KDColorBlack, backgroundColor, nodeNameLength);
// If it is needed, draw the parentheses
if (m_scriptNode->type() == ScriptNode::Type::WithParentheses) {
ctx->drawString(ScriptNodeCell::k_parentheses, KDPoint(nameSize.width(), nodeNameY), k_font, KDColorBlack, backgroundColor);
}
/* If it exists, draw the source name. If it did not fit, we would have put
* nullptr at the node creation. */
const char * sourceName = m_scriptNode->nodeSourceName();
if (sourceName != nullptr) {
KDSize sourceNameSize = k_font->stringSize(sourceName);
ctx->drawString(sourceName, KDPoint(m_frame.width() - sourceNameSize.width(), nodeNameY), k_font, Palette::CodeText, backgroundColor);
}
ctx->drawString(m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).fullName(), KDPoint(0, Metric::TableCellVerticalMargin + nameSize.height() + k_verticalMargin), k_font, Palette::SecondaryText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
}
KDSize ScriptNodeCell::ScriptNodeView::minimalSizeForOptimalDisplay() const {
if (m_scriptNode->name() == nullptr) {
return KDSizeZero;
}
KDSize size1 = k_font->stringSize(m_scriptNode->name());
KDSize size2 = k_font->stringSize(m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).fullName());
KDSize size3 = KDSizeZero;
if (m_scriptNode->type() == ScriptNode::Type::Function) {
size3 = k_font->stringSize(ScriptNodeCell::k_parentheses);
}
return KDSize(size1.width() + size3.width() > size2.width() ? size1.width() + size3.width() : size2.width(), Metric::TableCellVerticalMargin + size1.width() + k_verticalMargin + size2.width());
return KDSize(
k_optimalWidth,
m_scriptNode->description() == nullptr ? k_simpleItemHeight : k_complexItemHeight);
}
ScriptNodeCell::ScriptNodeCell() :
TableCell(),
m_scriptNodeView()
{
bool ScriptNodeCell::CanDisplayNameAndSource(int nameLength, const char * source) {
if (source == nullptr) {
return true;
}
assert(nameLength > 0);
const KDFont * font = ScriptNodeView::k_font;
return font->glyphSize().width()*(nameLength + 1) + font->stringSize(source).width() <= ScriptNodeView::k_optimalWidth; // + 1 for the separating space
}
void ScriptNodeCell::setScriptNode(ScriptNode * scriptNode) {
@@ -55,10 +59,6 @@ void ScriptNodeCell::setScriptNode(ScriptNode * scriptNode) {
reloadCell();
}
void ScriptNodeCell::setScriptStore(ScriptStore * scriptStore) {
m_scriptNodeView.setScriptStore(scriptStore);
}
void ScriptNodeCell::setHighlighted(bool highlight) {
TableCell::setHighlighted(highlight);
m_scriptNodeView.setHighlighted(highlight);

View File

@@ -10,9 +10,18 @@ namespace Code {
class ScriptNodeCell : public TableCell {
public:
ScriptNodeCell();
static_assert('\x11' == UCodePointEmpty, "Unicode error");
constexpr static char k_parentheses[] = "()";
constexpr static char k_parenthesesWithEmpty[] = "(\x11)";
constexpr static KDCoordinate k_simpleItemHeight = 27;
constexpr static KDCoordinate k_complexItemHeight = 42;
ScriptNodeCell() :
TableCell(),
m_scriptNodeView()
{}
void setScriptNode(ScriptNode * node);
void setScriptStore(ScriptStore * scriptStore);
static bool CanDisplayNameAndSource(int nameLength, const char * source);
/* TableCell */
View * labelView() const override { return const_cast<View *>(static_cast<const View *>(&m_scriptNodeView)); }
@@ -22,26 +31,25 @@ public:
void reloadCell() override;
const char * text() const override { return m_scriptNodeView.text(); }
static_assert('\x11' == UCodePointEmpty, "Unicode error");
constexpr static char k_parentheses[] = "()";
constexpr static char k_parenthesesWithEmpty[] = "(\x11)";
protected:
class ScriptNodeView : public HighlightCell {
public:
ScriptNodeView();
void setScriptNode(ScriptNode * scriptNode);
void setScriptStore(ScriptStore * scriptStore);
constexpr static const KDFont * k_font = KDFont::SmallFont;
constexpr static KDCoordinate k_optimalWidth = Ion::Display::Width - Metric::PopUpLeftMargin - Metric::PopUpRightMargin;
ScriptNodeView() :
HighlightCell(),
m_scriptNode(nullptr)
{}
void setScriptNode(ScriptNode * node) { m_scriptNode = node; }
void drawRect(KDContext * ctx, KDRect rect) const override;
virtual KDSize minimalSizeForOptimalDisplay() const override;
const char * text() const override {
return m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).fullName();
return m_scriptNode->description();
}
private:
constexpr static const KDFont * k_font = KDFont::SmallFont;
constexpr static KDCoordinate k_verticalMargin = 7;
constexpr static KDCoordinate k_bottomMargin = 5;
constexpr static KDCoordinate k_topMargin = k_bottomMargin + k_separatorThickness;
ScriptNode * m_scriptNode;
ScriptStore * m_scriptStore;
};
ScriptNodeView m_scriptNodeView;
};

View File

@@ -1,5 +1,6 @@
#include "script_parameter_controller.h"
#include "menu_controller.h"
#include <poincare/integer.h>
namespace Code {
@@ -11,6 +12,7 @@ ScriptParameterController::ScriptParameterController(Responder * parentResponder
m_autoImportScript(I18n::Message::AutoImportScript),
m_deleteScript(I18n::Message::DeleteScript),
m_duplicateScript(I18n::Message::DuplicateScript),
m_size(I18n::Message::ScriptSize),
m_selectableTableView(this),
m_script(Ion::Storage::Record()),
m_menuController(menuController)
@@ -43,21 +45,27 @@ bool ScriptParameterController::handleEvent(Ion::Events::Event event) {
m_menuController->renameSelectedScript();
return true;
case 2:
m_script.toggleImportationStatus();
m_script.toggleAutoimportationStatus();
m_selectableTableView.reloadData();
m_menuController->reloadConsole();
Container::activeApp()->setFirstResponder(&m_selectableTableView);
return true;
case 3:
dismissScriptParameterController();
m_menuController->deleteScript(s);
m_menuController->reloadConsole();
case 3:{
MessageTableCellWithBuffer * myCell = (MessageTableCellWithBuffer *)m_selectableTableView.selectedCell();
m_sizedisplaypercent = !m_sizedisplaypercent;
GetScriptSize(myCell);
return true;
}
case 4:
dismissScriptParameterController();
m_menuController->duplicateScript(s);
m_menuController->reloadConsole();
return true;
case 5:
dismissScriptParameterController();
m_menuController->deleteScript(s);
m_menuController->reloadConsole();
return true;
default:
assert(false);
return false;
@@ -80,14 +88,43 @@ void ScriptParameterController::didBecomeFirstResponder() {
HighlightCell * ScriptParameterController::reusableCell(int index) {
assert(index >= 0);
assert(index < k_totalNumberOfCell);
HighlightCell * cells[] = {&m_executeScript, &m_renameScript, &m_autoImportScript, &m_deleteScript, &m_duplicateScript};
HighlightCell * cells[] = {&m_executeScript, &m_renameScript, &m_autoImportScript, &m_size, &m_duplicateScript, &m_deleteScript};
return cells[index];
}
void ScriptParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) {
if (cell == &m_autoImportScript) {
SwitchView * switchView = (SwitchView *)m_autoImportScript.accessoryView();
switchView->setState(m_script.importationStatus());
switchView->setState(m_script.autoImportationStatus());
} else if (cell == &m_size) {
MessageTableCellWithBuffer * myCell = (MessageTableCellWithBuffer *)cell;
GetScriptSize(myCell);
myCell->setAccessoryFont(KDFont::SmallFont);
myCell->setAccessoryTextColor(Palette::SecondaryText);
}
}
void ScriptParameterController::GetScriptSize(MessageTableCellWithBuffer* myCell){
if(m_sizedisplaypercent){
char size[18];
int sizelen = Poincare::Integer((int)m_script.value().size).serialize(size, 6);
size[sizelen] = ' ';
size[sizelen+1] = 'o';
size[sizelen+2] = ' ';
size[sizelen+3] = '/';
size[sizelen+4] = ' ';
int sizelen2 = Poincare::Integer((int)Ion::Storage::k_storageSize).serialize(size+sizelen+5, 6) + sizelen + 5;
size[sizelen2] = ' ';
size[sizelen2+1] = 'o';
size[sizelen2+2] = '\0';
myCell->setAccessoryText(size);
}else{
char size[18];
int sizelen = Poincare::Integer((int)(((float)((int)m_script.value().size)/((int)Ion::Storage::k_storageSize)) * 100.f)).serialize(size, 3);
size[sizelen] = ' ';
size[sizelen+1] = '%';
size[sizelen+2] = '\0';
myCell->setAccessoryText(size);
}
}

View File

@@ -31,7 +31,7 @@ public:
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
private:
constexpr static int k_totalNumberOfCell = 5;
constexpr static int k_totalNumberOfCell = 6;
StackViewController * stackViewController();
I18n::Message m_pageTitle;
MessageTableCell m_executeScript;
@@ -39,9 +39,12 @@ private:
MessageTableCellWithSwitch m_autoImportScript;
MessageTableCell m_deleteScript;
MessageTableCell m_duplicateScript;
MessageTableCellWithBuffer m_size;
void GetScriptSize(MessageTableCellWithBuffer* myCell);
SelectableTableView m_selectableTableView;
Script m_script;
MenuController * m_menuController;
bool m_sizedisplaypercent = false;
};
}

View File

@@ -1,23 +1,14 @@
#include "script_store.h"
#include "string.h"
#include <stddef.h>
extern "C" {
#include "py/lexer.h"
#include "py/nlr.h"
}
namespace Code {
constexpr char ScriptStore::k_scriptExtension[];
bool ScriptStore::ScriptNameIsFree(const char * baseName) {
return Ion::Storage::sharedStorage()->recordBaseNamedWithExtension(baseName, k_scriptExtension).isNull();
return ScriptBaseNamed(baseName).isNull();
}
ScriptStore::ScriptStore()
{
ScriptStore::ScriptStore() {
addScriptFromTemplate(ScriptTemplate::Squares());
addScriptFromTemplate(ScriptTemplate::Parabola());
addScriptFromTemplate(ScriptTemplate::Mandelbrot());
@@ -34,118 +25,39 @@ bool ScriptStore::isFull() {
return Ion::Storage::sharedStorage()->availableSize() < k_fullFreeSpaceSizeLimit;
}
void ScriptStore::scanScriptsForFunctionsAndVariables(void * context, ScanCallback storeFunction, ScanCallback storeVariable) {
for (int scriptIndex = 0; scriptIndex < numberOfScripts(); scriptIndex++) {
// Handle lexer or parser errors with nlr.
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
const char * scriptContent = scriptAtIndex(scriptIndex).scriptContent();
if (scriptContent == nullptr) {
continue;
}
mp_lexer_t *lex = mp_lexer_new_from_str_len(0, scriptContent, strlen(scriptContent), false);
mp_parse_tree_t parseTree = mp_parse(lex, MP_PARSE_FILE_INPUT);
mp_parse_node_t pn = parseTree.root;
if (!MP_PARSE_NODE_IS_STRUCT(pn)) {
mp_parse_tree_clear(&parseTree);
nlr_pop();
continue;
}
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
// The script is only a single function definition.
if (((uint)(MP_PARSE_NODE_STRUCT_KIND(pns))) == k_functionDefinitionParseNodeStructKind) {
const char * id = structID(pns);
if (id == nullptr) {
continue;
}
storeFunction(context, id, scriptIndex);
mp_parse_tree_clear(&parseTree);
nlr_pop();
continue;
}
// The script is only a single global variable definition.
if (((uint)(MP_PARSE_NODE_STRUCT_KIND(pns))) == k_expressionStatementParseNodeStructKind) {
const char * id = structID(pns);
if (id == nullptr) {
continue;
}
storeVariable(context, id, scriptIndex);
mp_parse_tree_clear(&parseTree);
nlr_pop();
continue;
}
if (((uint)(MP_PARSE_NODE_STRUCT_KIND(pns))) != k_fileInput2ParseNodeStructKind) {
// The script node is not of type "file_input_2", thus it will not have main
// structures of the wanted type.
mp_parse_tree_clear(&parseTree);
nlr_pop();
continue;
}
// Count the number of structs in child nodes.
size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
for (size_t i = 0; i < n; i++) {
mp_parse_node_t child = pns->nodes[i];
if (MP_PARSE_NODE_IS_STRUCT(child)) {
mp_parse_node_struct_t *child_pns = (mp_parse_node_struct_t*)(child);
if (((uint)(MP_PARSE_NODE_STRUCT_KIND(child_pns))) == k_functionDefinitionParseNodeStructKind) {
const char * id = structID(child_pns);
if (id == nullptr) {
continue;
}
storeFunction(context, id, scriptIndex);
} else if (((uint)(MP_PARSE_NODE_STRUCT_KIND(child_pns))) == k_expressionStatementParseNodeStructKind) {
const char * id = structID(child_pns);
if (id == nullptr) {
continue;
}
storeVariable(context, id, scriptIndex);
}
}
}
mp_parse_tree_clear(&parseTree);
nlr_pop();
}
}
}
const char * ScriptStore::contentOfScript(const char * name) {
Script script = scriptNamed(name);
const char * ScriptStore::contentOfScript(const char * name, bool markAsFetched) {
Script script = ScriptNamed(name);
if (script.isNull()) {
return nullptr;
}
return script.scriptContent();
if (markAsFetched) {
script.setFetchedFromConsole(true);
}
return script.content();
}
void ScriptStore::clearVariableBoxFetchInformation() {
// TODO optimize fetches
const int scriptsCount = numberOfScripts();
for (int i = 0; i < scriptsCount; i++) {
scriptAtIndex(i).setFetchedForVariableBox(false);
}
}
void ScriptStore::clearConsoleFetchInformation() {
// TODO optimize fetches
const int scriptsCount = numberOfScripts();
for (int i = 0; i < scriptsCount; i++) {
scriptAtIndex(i).setFetchedFromConsole(false);
}
}
Script::ErrorStatus ScriptStore::addScriptFromTemplate(const ScriptTemplate * scriptTemplate) {
size_t valueSize = strlen(scriptTemplate->content())+1+1;// scriptcontent size + 1 char for the importation status
size_t valueSize = Script::StatusSize() + strlen(scriptTemplate->content()) + 1; // (auto importation status + content fetched status) + scriptcontent size + null-terminating char
assert(Script::nameCompliant(scriptTemplate->name()));
Script::ErrorStatus err = Ion::Storage::sharedStorage()->createRecordWithFullName(scriptTemplate->name(), scriptTemplate->value(), valueSize);
assert(err != Script::ErrorStatus::NonCompliantName);
return err;
}
const char * ScriptStore::structID(mp_parse_node_struct_t *structNode) {
// Find the id child node, which stores the struct's name
size_t childNodesCount = MP_PARSE_NODE_STRUCT_NUM_NODES(structNode);
if (childNodesCount < 1) {
return nullptr;
}
mp_parse_node_t child = structNode->nodes[0];
if (MP_PARSE_NODE_IS_LEAF(child)
&& MP_PARSE_NODE_LEAF_KIND(child) == MP_PARSE_NODE_ID)
{
uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(child);
return qstr_str(arg);
}
return nullptr;
}
}

View File

@@ -23,8 +23,11 @@ public:
Script scriptAtIndex(int index) {
return Script(Ion::Storage::sharedStorage()->recordWithExtensionAtIndex(k_scriptExtension, index));
}
Script scriptNamed(const char * name) {
return Script(Ion::Storage::sharedStorage()->recordNamed(name));
static Script ScriptNamed(const char * fullName) {
return Script(Ion::Storage::sharedStorage()->recordNamed(fullName));
}
static Script ScriptBaseNamed(const char * baseName) {
return Script(Ion::Storage::sharedStorage()->recordBaseNamedWithExtension(baseName, k_scriptExtension));
}
int numberOfScripts() {
return Ion::Storage::sharedStorage()->numberOfRecordsWithExtension(k_scriptExtension);
@@ -35,12 +38,10 @@ public:
void deleteAllScripts();
bool isFull();
/* Provide scripts content information */
typedef void (* ScanCallback)(void * context, const char * p, int n);
void scanScriptsForFunctionsAndVariables(void * context, ScanCallback storeFunction,ScanCallback storeVariable);
/* MicroPython::ScriptProvider */
const char * contentOfScript(const char * name) override;
const char * contentOfScript(const char * name, bool markAsFetched) override;
void clearVariableBoxFetchInformation();
void clearConsoleFetchInformation();
Ion::Storage::Record::ErrorStatus addScriptFromTemplate(const ScriptTemplate * scriptTemplate);
private:
@@ -51,10 +52,6 @@ private:
* importation status (1 char), the default content "from math import *\n"
* (20 char) and 10 char of free space. */
static constexpr int k_fullFreeSpaceSizeLimit = sizeof(Ion::Storage::record_size_t)+Script::k_defaultScriptNameMaxSize+k_scriptExtensionLength+1+20+10;
static constexpr size_t k_fileInput2ParseNodeStructKind = 1;
static constexpr size_t k_functionDefinitionParseNodeStructKind = 3;
static constexpr size_t k_expressionStatementParseNodeStructKind = 5;
const char * structID(mp_parse_node_struct_t *structNode);
};
}

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