mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[omega] 1.20.0
This commit is contained in:
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
84
.github/workflows/ci-workflow.yml
vendored
84
.github/workflows/ci-workflow.yml
vendored
@@ -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
38
.github/workflows/metric-workflow.yml
vendored
Normal 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
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
/build/artifacts/
|
||||
build/device/**/*.pyc
|
||||
epsilon.elf
|
||||
epsilon.map
|
||||
.vscode
|
||||
.DS_Store
|
||||
.gradle
|
||||
|
||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal 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
|
||||
61
Makefile
61
Makefile
@@ -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
|
||||
|
||||
48
README.md
48
README.md
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
18
apps/alternate_empty_nested_menu_controller.cpp
Normal file
18
apps/alternate_empty_nested_menu_controller.cpp
Normal 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;
|
||||
}
|
||||
19
apps/alternate_empty_nested_menu_controller.h
Normal file
19
apps/alternate_empty_nested_menu_controller.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Submodule apps/atom updated: 8fa51525b7...8f710a9d3f
@@ -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\
|
||||
|
||||
@@ -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));
|
||||
}, ¶meters, &th, false, Palette::GreyDark, false);
|
||||
}, ¶meters, &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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Calculation {
|
||||
class ComplexListController : public IllustratedListController {
|
||||
public:
|
||||
ComplexListController(EditExpressionController * editExpressionController) :
|
||||
IllustratedListController(nullptr, editExpressionController),
|
||||
IllustratedListController(editExpressionController),
|
||||
m_complexGraphCell(&m_model) {}
|
||||
|
||||
// ViewController
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Calculation {
|
||||
class IntegerListController : public ExpressionsListController {
|
||||
public:
|
||||
IntegerListController(EditExpressionController * editExpressionController) :
|
||||
ExpressionsListController(nullptr, editExpressionController) {}
|
||||
ExpressionsListController(editExpressionController) {}
|
||||
|
||||
//ListViewDataSource
|
||||
int numberOfRows() const override;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Calculation {
|
||||
class RationalListController : public ExpressionsListController {
|
||||
public:
|
||||
RationalListController(EditExpressionController * editExpressionController) :
|
||||
ExpressionsListController(nullptr, editExpressionController) {}
|
||||
ExpressionsListController(editExpressionController) {}
|
||||
|
||||
//ListViewDataSource
|
||||
int numberOfRows() const override;
|
||||
|
||||
@@ -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, ¢erFrame, &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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
155
apps/calculation/additional_outputs/unit_list_controller.cpp
Normal file
155
apps/calculation/additional_outputs/unit_list_controller.cpp
Normal 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(©, 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;
|
||||
}
|
||||
|
||||
}
|
||||
26
apps/calculation/additional_outputs/unit_list_controller.h
Normal file
26
apps/calculation/additional_outputs/unit_list_controller.h
Normal 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
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
9
apps/calculation/base.it.i18n
Normal file
9
apps/calculation/base.it.i18n
Normal 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"
|
||||
9
apps/calculation/base.nl.i18n
Normal file
9
apps/calculation/base.nl.i18n
Normal 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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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...) */
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
15
apps/code/base.it.i18n
Normal 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
15
apps/code/base.nl.i18n
Normal 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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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?"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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é"
|
||||
|
||||
@@ -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
217
apps/code/catalog.it.i18n
Normal 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
217
apps/code/catalog.nl.i18n
Normal 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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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\")"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user