├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── script-addition └── pull_request_template.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── images ├── highbug.png ├── picture.png ├── picture1.png ├── picture2.png ├── picture3.png ├── picture5.png ├── picture8.png ├── picture9.png ├── q.png ├── quiet4.png ├── quiet6.png ├── quiet7.png ├── themepic1.png ├── themepic2.png ├── themepic3.png ├── themes.png └── windows.JPG ├── requirements.txt └── src ├── data ├── config │ ├── settings-default.yaml │ └── settings.yaml ├── q.png └── theme_configs │ ├── dark-heart.yaml │ ├── desert.yaml │ ├── dracula.yaml │ ├── githubly.yaml │ ├── gruvbox.yaml │ ├── material.yaml │ ├── monokai.yaml │ ├── monokai_pro.yaml │ ├── pumpkin.yaml │ ├── rust.yaml │ └── solarized.yaml ├── quiet_context.py ├── quiet_find.py ├── quiet_linenumbers.py ├── quiet_loaders.py ├── quiet_main.py ├── quiet_menubar.py ├── quiet_statusbar.py ├── quiet_syntax_and_themes.py ├── quiet_syntax_highlighting.py ├── quiet_textarea.py └── quiet_tree.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Operating system :** 27 | - OS: [e.g. Windows, Linux, Mac] 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/script-addition: -------------------------------------------------------------------------------- 1 | --- 2 | name: Script Addition 3 | about: Describe this Script you want to add 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Aim 11 | 12 | - What is the objective of the Script 13 | 14 | # Details 15 | 16 | - What the features will your script have 17 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Description 8 | 9 | Please include a summary of the change and which issue is fixed. List any dependencies that are required for this change. 10 | 11 | ## Fixes (if any) #(issue_no) 12 | 13 | Replace `issue_no` with the issue number which is fixed in this PR 14 | 15 | ## Have you read the [Contributing Guidelines on Pull Requests](https://github.com/SethWalkeroo/Quiet-Text/blob/main/CONTRIBUTING.md)? 16 | 17 | - [ ] Yes 18 | - [ ] No 19 | 20 | ## Type of change 21 | 22 | _Please delete options that are not relevant._ 23 | 24 | - [ ] Bug fix (non-breaking change which fixes an issue) 25 | - [ ] New feature (non-breaking change which adds functionality) 26 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 27 | - [ ] This change requires a documentation update 28 | - [ ] Documentation Update 29 | 30 | ## Checklist: 31 | 32 | - [ ] My works and is relatively clean. 33 | - [ ] I have performed a self-review of my own code 34 | - [ ] I have commented my code, particularly in hard-to-understand areas 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytype static type analyzer 136 | .pytype/ 137 | 138 | # Cython debug symbols 139 | cython_debug/ 140 | 141 | # Ignore test file 142 | TEST_FILE.txt 143 | test.py 144 | test.c 145 | test.js 146 | test.txt 147 | test.cpp 148 | test.css 149 | test.html 150 | test.go 151 | test.txt 152 | test 153 | test.java 154 | learn.js 155 | test.md 156 | test.rs 157 | test.sql 158 | test.swift 159 | test.hs 160 | test.coffee 161 | test.dart 162 | .vscode 163 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at sethrobertwalker@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | This documentation contains a set of guidelines to help you during the contribution process. 4 | We are happy to welcome all the contributions from anyone willing to improve/add new scripts to this project. Thank you for helping out and remember, 5 | **no contribution is too small.** 6 | 7 | # Submitting Contributions 8 | Below you will find the process and workflow used to review and merge your changes. 9 | ## Step 1 : Find an issue 10 | - Take a look at the Existing Issues or create your **own** Issues! 11 | - Wait for the Issue to be assigned to you after which you can start working on it. 12 | - Note : Every change in this project should/must have an associated issue. 13 | 14 | 15 | ## Step 2 : Fork the Project 16 | - Fork this Repository. This will create a Local Copy of this Repository on your Github Profile. Keep a reference to the original project in `upstream` remote. 17 | ``` 18 | $ git clone https://github.com//Quiet-Text 19 | $ cd QuietTxt 20 | $ git remote add upstream https://github.com/sethwalkeroo/Quiet-Text 21 | ``` 22 | - If you have already forked the project, update your copy before working. 23 | ``` 24 | $ git remote update 25 | $ git checkout 26 | $ git rebase upstream/ 27 | ``` 28 | ## Step 3 : Branch 29 | Create a new branch. Use its name to identify the issue your addressing. 30 | ``` 31 | # It will create a new branch with name Branch_Name and switch to that branch 32 | $ git checkout -b branch_name 33 | ``` 34 | ## Step 4 : Work on the issue assigned 35 | - Work on the issue(s) assigned to you. 36 | - Add all the files/folders needed. 37 | - After you've made changes or made your contribution to the project add changes to the branch you've just created by: 38 | ``` 39 | # To add all new files to branch Branch_Name 40 | $ git add . 41 | ``` 42 | ## Step 5 : Commit 43 | - To commit give a descriptive message for the convenience of reveiwer by: 44 | ``` 45 | # This message get associated with all files you have changed 46 | $ git commit -m 'message 47 | ``` 48 | - **NOTE**: A PR should have only one commit. Multiple commits should be squashed. 49 | ## Step 6 : Work Remotely 50 | - Now you are ready to your work to the remote repository. 51 | - When your work is ready and complies with the project conventions, upload your changes to your fork: 52 | 53 | ``` 54 | # To push your work to your remote repository 55 | $ git push -u origin Branch_Name 56 | ``` 57 | 58 | ## Step 7 : Pull Request 59 | - Go to your repository in browser and click on compare and pull requests. Then add a title and description to your pull request that explains your contribution. 60 | 61 | - Voila! Your Pull Request has been submitted and will be reviewed by the moderators and merged. 62 | 63 | ## Need more help? 64 | You can refer to the following articles on basics of Git and Github and also contact the Project Mentors, in case you are stuck: 65 | - [Forking a Repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) 66 | - [Cloning a Repo](https://help.github.com/en/desktop/contributing-to-projects/creating-an-issue-or-pull-request) 67 | - [How to create a Pull Request](https://opensource.com/article/19/7/create-pull-request-github) 68 | - [Getting started with Git and GitHub](https://towardsdatascience.com/getting-started-with-git-and-github-6fcd0f2d4ac6) 69 | - [Learn GitHub from Scratch](https://lab.github.com/githubtraining/introduction-to-github) 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Seth Walker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Quiet Text logo 3 | 4 |

5 | 6 |

Quiet Text

7 | 8 |

9 | GitHub top language 10 | GitHub issues 11 | GitHub pull requests 12 | Lines of code 13 |

14 | 15 |

16 | Quiet Text is a simple, minimalist text editor made with Python's Tkinter GUI library. Quiet Text aims to create a calming and distraction free text environment for writing code and taking notes. 17 |

18 | 19 | there should be an image here... 20 | (screenshot taken on Windows 10) 21 | 22 | ## Table of contents 23 | - [Notable Features](#notable-features) 24 | - [Keyboard Shortcuts and Usage](#keyboard-shortcuts) 25 | - [Quick Start](#quick-start) 26 | - [Installation/Setup](#installation) 27 | - [Contributing](#contributing) 28 | 29 | ## About 30 | 31 | Quiet Text aims to give you a **clean and simple** text editor experience. Everything is right in front of you and all completely customizable to your preferences. Too many text editors clutter their ui with unnecessary tooling that overwhelms their users. It isn't uncommon for somebody to lose hours of their precious work day mulling over and disabling irritating configuration settings in their text editor/ide. We want to eliminate the annoyances of a noisy/busy text editing environment and bring you an environment that has the **main goal of increasing focus and eliminating distractions**. 32 | 33 | ## Notable Features 34 | 35 | - Autocompletion for parenthesis, brackets (square and curly), single quotes, and double quotes. 36 | - Auto indentation for code blocks and brackets (when creating lists or creating functions). 37 | - Syntax highlighting for 15 different languages and more to come (Go, Python, C, C++, Dart, CoffeeScript, Haskell, Rust, Java, JavaScript, HTML, Sql, CSS, Yaml, Markdown). 38 | - Ability to compile and run your code from the editor. 39 | - 11 different themes and color schemes to chose from (and more to come). 40 | - Customization options like font, text color, line height, and many more. 41 | 42 | 43 | ## Keyboard Shortcuts 44 | 45 | 46 | Quiet Text has shortcuts for most commonly performed actions. The list of all the shortcuts is presented bellow: 47 | 48 | | Command | KeyBinding | Description | 49 | | ------- | ---------- | ----------- | 50 | | Copy | ctrl+c | Copy selected text | 51 | | Cut | ctrl+x | Cut selected text | 52 | | Undo | ctrl+z | undo edits to the text area | 53 | | Redo | ctrl+y | redo edits to the text area on Windows | 54 | | Paste | ctrl+v | Paste text from the clipboard | 55 | | Bold | ctrl+b | Bold selected text | 56 | | Find and Replace | ctrl+f | Find and replace specified text | 57 | | Highlight | ctrl+h | Highlight selected text | 58 | | Hide Menu | alt | Hides menu bar from view in the text editor | 59 | | Hide Line Numbers | ctrl+shift+l | Hides line numbers from text area | 60 | | New File | ctrl+n | Open a new empty file | 61 | | Load Previous File | ctrl+p | Loads the last file you had open | 62 | | Open File | ctrl+o | Open an existing file | 63 | | Open File Tree | ctrl+t | Opens file tree for seamless work inside a directory | 64 | | Color Menu | ctrl+m | Opens color menu | 65 | | Run File | ctrl+r | Run the currently active file | 66 | | Save | ctrl+s | Save the currently active file | 67 | | Save As | ctrl+shift+s | Save the currently active file under a different name | 68 | | Change Font Size | ctrl+mousewheel | Increases or decrease font size | 69 | | Indent | tab | Indent one or multiple lines | 70 | | Unindent | shift+tab | Unindent one or multiple lines | 71 | 72 | 73 | ## Quick Start 74 | 75 | ### Use the release binaries (.exe) 76 | 77 | The fastest way to use the program is to just launch one of the release binaries on your system. 78 | If you are on Linux and the binary is not opening you may need to allow permission first. Type in the following command **if you trust this program**. 79 | On Windows you may run into a similar issue, but the Windows gui will let you "run anyway" if you allow it. 80 | 81 | ```sh 82 | chmod u+x (name of .exe file) 83 | ``` 84 | 85 | ### Launch using python 86 | The second fastest way would be to install the dependencies through **requirements.txt** and then launch the application through **src/quiet_main.py**. 87 | You can see instructions for this method [below](#installation). 88 | 89 | ### Create your own binary 90 | If the release binary is not up to date with the current development branch, then you can just create your own binary with **pyinstaller**. I'll walk you through the process real quick. 91 | 92 | 1. Clone source files off github into your preferred directory 93 | ```sh 94 | git clone https://github.com/SethWalkeroo/Quiet-Text.git 95 | ``` 96 | 97 | 2. Install pyinstaller 98 | ```sh 99 | pip install pyinstaller 100 | ``` 101 | 102 | 3. Cd into the src directory and use pyinstaller on quiet_main.py 103 | ```sh 104 | pyinstaller --onefile quiet_main.py 105 | ``` 106 | 107 | 4. Edit the quiet_main.spec file to include the projects resource tree (configs, themes, syntax, etc.) 108 | 109 | ![](https://media.giphy.com/media/ZS6SKVVCrB6UHzmY6S/giphy.gif) 110 | (Sorry for the low quality gif. Hopefully it works well enough.) 111 | 112 | 5. Use pyinstaller again on your new quiet_main.spec file 113 | 114 | ```sh 115 | pyinstaller quiet_main.spec 116 | ``` 117 | 118 | 6. Check the dist directory for your binary file and you should be good to go! 119 | 120 | 121 | ## Installation 122 | This project requires **Pygments** as well as **PyYAML**. Both of these packages can be installed through a virtual environment with **requirements.txt**. 123 | After you install the dependencies, you can simply head into the **src** directory and launch the editor from **src/quiet_main.py**. 124 | 125 | **Mac and Linux installation:** 126 | 127 | ```sh 128 | python3 -m venv env 129 | ``` 130 | 131 | ```sh 132 | source env/bin/activate 133 | ``` 134 | 135 | ```sh 136 | pip3 install -r requirements.txt 137 | ``` 138 | ```sh 139 | cd src 140 | ``` 141 | 142 | ```sh 143 | python3 quiet_app_launch.py 144 | ``` 145 | 146 | **Windows:** 147 | 148 | ```sh 149 | python -m venv env 150 | ``` 151 | 152 | ``` 153 | env\Scripts\activate.bat 154 | ``` 155 | ``` 156 | pip install -r requirements.txt 157 | ``` 158 | 159 | ```sh 160 | cd src 161 | ``` 162 | 163 | ```sh 164 | python3 quiet_main.py 165 | ``` 166 | 167 | This project also requires a Python3 interpreter with Tkinter support. 168 | You can test it using 169 | 170 | ```sh 171 | python3 -m tkinter 172 | ``` 173 | 174 | In case your (GNU/Linux) machine does not support tkinter, there's a way to run it inside Docker (whereas you can also use another base image than `ubuntu`): 175 | 176 | ``` 177 | FROM ubuntu 178 | RUN apt update && apt install -y python3-tk x11-apps 179 | RUN mkdir /code 180 | WORKDIR /code 181 | ADD . /code 182 | CMD ["/usr/bin/python3", "-m", "quiet"] 183 | ``` 184 | 185 | Now, expose an environment variable to allow access to your host system `XAUTH="$HOME/.Xauthority"` and build the image using `docker build -t quiet .`. 186 | You can start a container using 187 | 188 | ```sh 189 | docker run --network=host --rm -e DISPLAY=$DISPLAY -v $XAUTH:/root/.Xauthority quiet 190 | ``` 191 | 192 | Be aware, that the Docker container has full access to your machine! So you better trust the executed code. 193 | 194 | 195 | 196 | 197 | 198 | ## Contributing 199 | 200 | * Issues are open to anyone and everyone, but you must comment on the issue first and communicate to me that you are working on it. If you are confident in your ability, I will assign you to the issue. 201 | 202 | * Don't work on an issue that isn't assigned to you unless you communicate with the assignee first. 203 | 204 | * If you make an improvement on an existing feature, make sure to create an issue first and list the fixes or features you have made to the code. 205 | 206 | * All PRs must be made from a Branch. Create a separate branch for every Issue you are working upon and once found fit, make a PR. 207 | 208 | * Please make sure your code works before you submit it :) 209 | 210 | #### check CONTRIBUTING.md for guidlines on how to make a pull request. 211 | 212 | 213 | 214 | ## Goals 215 | 216 | - [x] Create a visually pleasing text editor! ;) 217 | - [x] Add syntax highlighting for Python. 218 | - [ ] Allow user's to launch the terminal and run their scripts from any platform. 219 | - [ ] Add special markdown for making lists and notetaking. 220 | - [x] Allow full customization of the editor's theme and colors. 221 | - [x] Add helpful features for programming like autoclosing brackets and parenthesis. 222 | 223 | #### suggestions are welcome! 224 | 225 | 226 | -------------------------------------------------------------------------------- /images/highbug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/highbug.png -------------------------------------------------------------------------------- /images/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/picture.png -------------------------------------------------------------------------------- /images/picture1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/picture1.png -------------------------------------------------------------------------------- /images/picture2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/picture2.png -------------------------------------------------------------------------------- /images/picture3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/picture3.png -------------------------------------------------------------------------------- /images/picture5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/picture5.png -------------------------------------------------------------------------------- /images/picture8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/picture8.png -------------------------------------------------------------------------------- /images/picture9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/picture9.png -------------------------------------------------------------------------------- /images/q.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/q.png -------------------------------------------------------------------------------- /images/quiet4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/quiet4.png -------------------------------------------------------------------------------- /images/quiet6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/quiet6.png -------------------------------------------------------------------------------- /images/quiet7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/quiet7.png -------------------------------------------------------------------------------- /images/themepic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/themepic1.png -------------------------------------------------------------------------------- /images/themepic2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/themepic2.png -------------------------------------------------------------------------------- /images/themepic3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/themepic3.png -------------------------------------------------------------------------------- /images/themes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/themes.png -------------------------------------------------------------------------------- /images/windows.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/images/windows.JPG -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | install==1.3.4 2 | Pygments==2.7.1 3 | PyYAML==5.3.1 4 | -------------------------------------------------------------------------------- /src/data/config/settings-default.yaml: -------------------------------------------------------------------------------- 1 | autoclose_curlybraces: true 2 | autoclose_doublequotes: true 3 | autoclose_parentheses: true 4 | autoclose_singlequotes: true 5 | autoclose_squarebrackets: true 6 | current_line_indicator: false 7 | current_line_indicator_symbol: '>' 8 | font_family: consolas 9 | font_size: 11 10 | horizontal_scrollbar_show: true 11 | horizontal_scrollbar_width: 8 12 | insertion_blink: false 13 | tab_size: 4 14 | text_bottom_lineheight: '6' 15 | text_top_lineheight: '0' 16 | text_wrap: none 17 | textarea_border: 0 18 | textarea_padding_x: '5' 19 | textarea_padding_y: '5' 20 | vertical_scrollbar_show: 'true' 21 | vertical_scrollbar_width: 8 22 | web_browser: chromium 23 | theme: './data/theme_configs/dark-heart.yaml' 24 | -------------------------------------------------------------------------------- /src/data/config/settings.yaml: -------------------------------------------------------------------------------- 1 | autoclose_curlybraces: true 2 | autoclose_doublequotes: true 3 | autoclose_parentheses: true 4 | autoclose_singlequotes: true 5 | autoclose_squarebrackets: true 6 | current_line_indicator: false 7 | current_line_indicator_symbol: '>' 8 | font_family: consolas 9 | font_size: 11 10 | horizontal_scrollbar_show: true 11 | horizontal_scrollbar_width: 8 12 | insertion_blink: false 13 | tab_size: 4 14 | text_bottom_lineheight: '6' 15 | text_top_lineheight: '0' 16 | text_wrap: none 17 | textarea_border: 0 18 | textarea_padding_x: '5' 19 | textarea_padding_y: '5' 20 | vertical_scrollbar_show: 'true' 21 | vertical_scrollbar_width: 8 22 | web_browser: chromium 23 | theme: './data/theme_configs/dark-heart.yaml' 24 | -------------------------------------------------------------------------------- /src/data/q.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SethWalkeroo/Quiet-Text/1995acb0d30665d20c12d554a0f2d56965294517/src/data/q.png -------------------------------------------------------------------------------- /src/data/theme_configs/dark-heart.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#42be65' 2 | string_color: '#82cfff' 3 | number_color: '#d02670' 4 | type_color: '#d02670' 5 | keyword_color: '#8a3ffc' 6 | operator_color: '#8a3ffc' 7 | bultin_function_color: '#0f62fe' 8 | class_self_color: '#78a9ff' 9 | namespace_color: '#8a3ffc' 10 | class_name_color: '#3ddbd9' 11 | function_name_color: '#3ddbd9' 12 | font_color: '#ffffff' 13 | bg_color: '#161616' 14 | menu_fg_active: '#ffffff' 15 | menu_bg_active: '#001d6c' 16 | selection_color: '#2b4532' 17 | -------------------------------------------------------------------------------- /src/data/theme_configs/desert.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#928374' 2 | string_color: '#ffa0a0' 3 | number_color: '#cdb2ff' 4 | type_color: '#46910c' 5 | keyword_color: '#f0e68c' 6 | operator_color: '#98fb98' 7 | bultin_function_color: '#66D9EF' 8 | class_self_color: '#bbbbbb' 9 | namespace_color: '#e38f00' 10 | class_name_color: '#A6E22E' 11 | function_name_color: '#98fb98' 12 | font_color: '#F8F8F2' 13 | bg_color: '#1c1c18' 14 | menu_fg_active: '#f0e68c' 15 | menu_bg_active: '#282921' 16 | selection_color: '#282921' -------------------------------------------------------------------------------- /src/data/theme_configs/dracula.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#6272a4' 2 | string_color: '#f1fa8c' 3 | number_color: '#bd93f9' 4 | type_color: '#bd93f9' 5 | keyword_color: '#ff79c6' 6 | operator_color: '#ff79c6' 7 | bultin_function_color: '#8be9fd' 8 | class_self_color: '#bd93f9' 9 | namespace_color: '#ff79c6' 10 | class_name_color: '#50fa7b' 11 | function_name_color: '#50fa7b' 12 | font_color: '#f8f8f2' 13 | bg_color: '#282a36' 14 | menu_fg_active: '#50fa7b' 15 | menu_bg_active: '#382b45' 16 | selection_color: '#382b45' 17 | -------------------------------------------------------------------------------- /src/data/theme_configs/githubly.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#6A737D' 2 | string_color: '#032F62' 3 | number_color: '#005CC5' 4 | type_color: '#005CC5' 5 | keyword_color: '#D73A49' 6 | operator_color: '#D73A49' 7 | bultin_function_color: '#D73A49' 8 | class_self_color: '#24292E' 9 | namespace_color: '#D73A49' 10 | class_name_color: '#E36209' 11 | function_name_color: '#E36209' 12 | font_color: '#24292E' 13 | bg_color: '#FFFFFF' 14 | menu_fg_active: '#24292E' 15 | menu_bg_active: '#c1d9e3' 16 | selection_color: '#c1d9e3' 17 | -------------------------------------------------------------------------------- /src/data/theme_configs/gruvbox.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#3c3836' 2 | string_color: '#b8bb26' 3 | number_color: '#b16286' 4 | type_color: '#458588' 5 | keyword_color: '#d65d0e' 6 | operator_color: '#d79921' 7 | bultin_function_color: '#689d6a' 8 | class_self_color: '#d3869b' 9 | namespace_color: '#d65d0e' 10 | class_name_color: '#b8bb26' 11 | function_name_color: '#b8bb26' 12 | font_color: '#fbf1c7' 13 | bg_color: '#1d2021' 14 | menu_fg_active: '#d79921' 15 | menu_bg_active: '#282828' 16 | selection_color: '#32302f' 17 | -------------------------------------------------------------------------------- /src/data/theme_configs/material.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#546e7a' 2 | string_color: '#c3e88d' 3 | number_color: '#f78c6c' 4 | type_color: '#f07178' 5 | keyword_color: '#c792ea' 6 | operator_color: '#89ddff' 7 | bultin_function_color: '#c792ea' 8 | class_self_color: '#f07178' 9 | namespace_color: '#89ddff' 10 | class_name_color: '#82aaff' 11 | function_name_color: '#82aaff' 12 | font_color: '#FFFFFF' 13 | bg_color: '#212121' 14 | menu_fg_active: '#80cbc4' 15 | menu_bg_active: '#323232' 16 | selection_color: '#353535' 17 | -------------------------------------------------------------------------------- /src/data/theme_configs/monokai.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#75715E' 2 | string_color: '#e6db74' 3 | number_color: '#ae81ff' 4 | type_color: '#fd971f' 5 | keyword_color: '#f92672' 6 | operator_color: '#f92672' 7 | bultin_function_color: '#66d9ef' 8 | class_self_color: '#fd971f' 9 | namespace_color: '#f92672' 10 | class_name_color: '#a6e22e' 11 | function_name_color: '#a6e22e' 12 | font_color: '#f8f8f2' 13 | bg_color: '#272822' 14 | menu_fg_active: '#a1efe4' 15 | menu_bg_active: '#36342c' 16 | selection_color: '#36342c' 17 | 18 | -------------------------------------------------------------------------------- /src/data/theme_configs/monokai_pro.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#75715E' 2 | string_color: '#FFD866' 3 | number_color: '#AB9DF2' 4 | type_color: '#fc9867' 5 | keyword_color: '#F92672' 6 | operator_color: '#F92672' 7 | bultin_function_color: '#78DCE8' 8 | class_self_color: '#c9bfbd' 9 | namespace_color: '#F92672' 10 | class_name_color: '#A9DC76' 11 | function_name_color: '#A9DC76' 12 | font_color: '#f8f8f2' 13 | bg_color: '#2e2424' 14 | menu_fg_active: '#e6db74' 15 | menu_bg_active: '#292e2b' 16 | selection_color: '#524240' 17 | -------------------------------------------------------------------------------- /src/data/theme_configs/pumpkin.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#8c7d64' 2 | string_color: '#ebc88d' 3 | number_color: '#ebc88d' 4 | type_color: '#ebc88d' 5 | keyword_color: '#e38f00' 6 | operator_color: '#e38f00' 7 | bultin_function_color: '#e38f00' 8 | class_self_color: '#d4c3a7' 9 | namespace_color: '#e38f00' 10 | class_name_color: '#b58900' 11 | function_name_color: '#b58900' 12 | font_color: '#a3844e' 13 | bg_color: '#403f30' 14 | menu_fg_active: '#cc8100' 15 | menu_bg_active: '#61583e' 16 | selection_color: '#61583e' 17 | -------------------------------------------------------------------------------- /src/data/theme_configs/rust.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#453d36' 2 | string_color: '#45c91c' 3 | number_color: '#f5f24c' 4 | keyword_color: '#8f5c00' 5 | type_color: '#0e9c5c' 6 | operator_color: '#d14c04' 7 | bultin_function_color: '#8f5c00' 8 | class_self_color: '#d4c3a7' 9 | namespace_color: '#d14c04' 10 | class_name_color: '#ffb126' 11 | function_name_color: '#ffb126' 12 | font_color: '#e6d6ba' 13 | bg_color: '#0b0e12' 14 | menu_fg_active: '#d14c04' 15 | menu_bg_active: '#1f1c1a' 16 | selection_color: '#1f1c1a' 17 | -------------------------------------------------------------------------------- /src/data/theme_configs/solarized.yaml: -------------------------------------------------------------------------------- 1 | comment_color: '#586e75' 2 | string_color: '#2aa198' 3 | number_color: '#6c71c4' 4 | type_color: '#d33682' 5 | keyword_color: '#859900' 6 | operator_color: '#dc322f' 7 | bultin_function_color: '#859900' 8 | class_self_color: '#d33682' 9 | namespace_color: '#cb4b16' 10 | class_name_color: '#b58900' 11 | function_name_color: '#b58900' 12 | font_color: '#839496' 13 | bg_color: '#002b36' 14 | menu_fg_active: '#859900' 15 | menu_bg_active: '#073642' 16 | selection_color: '#193b45' 17 | -------------------------------------------------------------------------------- /src/quiet_context.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.font as tk_font 3 | import os 4 | from tkinter import ttk 5 | 6 | class ContextMenu(tk.Listbox): 7 | #init function 8 | def __init__(self, parent, *args, **kwargs): 9 | tk.Listbox.__init__(self, parent, *args, **kwargs) 10 | 11 | self.font_family = parent.font_family 12 | self.font_color = parent.menu_fg 13 | self.bg_color = parent.bg_color 14 | self.active_bg = parent.menubar_bg_active 15 | self.active_fg = parent.menubar_fg_active 16 | self.parent = parent 17 | 18 | self.changes = [""] 19 | self.steps = int() 20 | 21 | # setting tk.RIGHT click menu bar 22 | self.right_click_menu = tk.Menu( 23 | parent, 24 | font='DroidSansFallback', 25 | fg=self.font_color, 26 | bg=self.bg_color, 27 | activebackground=self.active_bg, 28 | activeforeground=self.active_fg, 29 | bd=0, 30 | tearoff=0) 31 | 32 | self.right_click_menu.add_command( 33 | label='Cut', 34 | command=self.parent.textarea.event_generate('<>')) 35 | 36 | self.right_click_menu.add_command( 37 | label='Copy', 38 | command=self.parent.textarea.event_generate('<>')) 39 | 40 | self.right_click_menu.add_command( 41 | label='Paste', 42 | command=self.parent.textarea.event_generate('<>')) 43 | 44 | self.right_click_menu.add_command( 45 | label='Bold', 46 | command=self.bold) 47 | 48 | self.right_click_menu.add_command( 49 | label='Highlight', 50 | command=self.hightlight) 51 | 52 | def popup(self, event): 53 | try: 54 | self.right_click_menu.tk_popup(event.x_root, event.y_root, 0) 55 | finally: 56 | self.right_click_menu.grab_release() 57 | 58 | def undo(self, event=None): 59 | if self.steps != 0: 60 | self.steps -= 1 61 | self.parent.textarea.delete(0, tk.END) 62 | self.parent.textarea.insert(tk.END, self.changes[self.steps]) 63 | 64 | def redo(self, event=None): 65 | if self.steps < len(self.changes): 66 | self.parent.textarea.delete(0, tk.END) 67 | self.parent.textarea.insert(tk.END, self.changes[self.steps]) 68 | self.steps += 1 69 | 70 | def add_changes(self, event=None): 71 | if self.parent.textarea.get() != self.changes[-1]: 72 | self.changes.append(self.parent.textarea.get()) 73 | self.steps += 1 74 | 75 | # Setting the selected text to be bold 76 | def bold(self, event=None): 77 | if self.parent.filename: 78 | try: 79 | if(os.path.splitext(self.parent.filename)[1][1:] == "txt"): 80 | current_tags = self.parent.textarea.tag_names("sel.first") 81 | bold_font = tk_font.Font(self.parent.textarea, self.parent.textarea.cget("font")) 82 | bold_font.configure(weight = "bold") 83 | self.parent.textarea.tag_config("bold", font = bold_font) 84 | if "bold" in current_tags: 85 | self.parent.textarea.tag_remove("bold", "sel.first", "sel.last") 86 | else: 87 | self.parent.textarea.tag_add("bold", "sel.first", "sel.last") 88 | else: 89 | self.parent.statusbar.update_status('no txt bold') 90 | except tk.TclError: 91 | pass 92 | else: 93 | self.parent.statusbar.update_status('no file') 94 | 95 | def hightlight(self, event=None): 96 | if self.parent.filename: 97 | try: 98 | if(os.path.splitext(self.parent.filename)[1][1:] == "txt"): 99 | new_color = self.parent.menubar.open_color_picker() 100 | current_tags = self.parent.textarea.tag_names("sel.first") 101 | highlight_font = tk_font.Font(self.parent.textarea, self.parent.textarea.cget("font")) 102 | self.parent.textarea.tag_config( 103 | f"highlight_{new_color}", 104 | font = highlight_font, 105 | foreground = "black", 106 | background = new_color) 107 | if "highlight" in current_tags: 108 | for tag in current_tags: 109 | if "highlight" in tag: 110 | print(tag) 111 | self.parent.textarea.tag_remove(tag, "sel.first", "sel.last") 112 | else: 113 | self.parent.textarea.tag_add("highlight", "sel.first", "sel.last") 114 | self.parent.textarea.tag_add(f"highlight_{new_color}","sel.first", "sel.last") 115 | else: 116 | self.parent.statusbar.update_status('no txt high') 117 | except tk.TclError: 118 | pass 119 | else: 120 | self.parent.statusbar.update_status('no file') 121 | 122 | -------------------------------------------------------------------------------- /src/quiet_find.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.ttk as ttk 3 | import os 4 | from quiet_loaders import QuietLoaders 5 | 6 | 7 | class FindWindow(tk.Toplevel): 8 | def __init__(self, master, **kwargs): 9 | super().__init__(master, **kwargs) 10 | 11 | self.master = master 12 | self.loader = QuietLoaders() 13 | self.geometry('260x100') 14 | self.minsize(260, 100) 15 | self.maxsize(260, 100) 16 | self.quiet_icon_path = self.loader.resource_path( 17 | os.path.join('data', 'q.png')) 18 | self.icon = tk.PhotoImage(file=self.quiet_icon_path) 19 | self.iconphoto(False, self.icon) 20 | self.title('Search and Replace') 21 | self.transient(master) 22 | self.configure(bg=master.bg_color) 23 | self.style = ttk.Style() 24 | self.style.theme_use('clam') 25 | self.bg_color = master.bg_color 26 | self.fg_color = master.fg_color 27 | self.active_fg = master.active_fg 28 | self.active_bg = master.active_bg 29 | 30 | self.text_to_find = tk.StringVar() 31 | self.text_to_replace_with = tk.StringVar() 32 | 33 | top_frame = tk.Frame(self, bg=self.bg_color) 34 | middle_frame = tk.Frame(self, bg=self.bg_color) 35 | bottom_frame = tk.Frame(self, bg=self.bg_color) 36 | 37 | self.style.configure( 38 | 'editor.TLabel', 39 | background=self.bg_color, 40 | foreground='#fff',) 41 | 42 | find_entry_label = ttk.Label(top_frame, text="Search: ", style="editor.TLabel") 43 | self.find_entry = ttk.Entry(top_frame, textvar=self.text_to_find) 44 | 45 | replace_entry_label = ttk.Label(middle_frame, text="Replace: ", style="editor.TLabel") 46 | self.replace_entry = ttk.Entry(middle_frame, textvar=self.text_to_replace_with) 47 | 48 | self.style.configure('editor.TButton', 49 | background=self.bg_color, 50 | foreground='#fff', 51 | activeforeground=self.active_fg) 52 | 53 | self.style.map('editor.TButton', 54 | background=[('pressed', self.active_fg), ('active', self.active_bg)]) 55 | 56 | self.find_button = ttk.Button(bottom_frame, text="Search", command=self.on_find, style="editor.TButton") 57 | self.replace_button = ttk.Button(bottom_frame, text="Replace", command=self.on_replace, style="editor.TButton") 58 | self.cancel_button = ttk.Button(bottom_frame, text="Cancel", command=self.on_cancel, style="editor.TButton") 59 | 60 | find_entry_label.pack(side=tk.LEFT, padx=(5, 12)) 61 | self.find_entry.pack(side=tk.LEFT, fill=tk.X, expand=1, padx=(0, 5)) 62 | 63 | replace_entry_label.pack(side=tk.LEFT, padx=(5, 5)) 64 | self.replace_entry.pack(side=tk.LEFT, fill=tk.X, expand=1, padx=(0, 5)) 65 | 66 | self.find_button.pack(side=tk.LEFT, padx=(5, 0)) 67 | self.replace_button.pack(side=tk.LEFT, padx=(5, 0)) 68 | self.cancel_button.pack(side=tk.LEFT, padx=(5, 5)) 69 | 70 | top_frame.pack(side=tk.TOP, expand=1, fill=tk.X, padx=0) 71 | middle_frame.pack(side=tk.TOP, expand=1, fill=tk.X, padx=0) 72 | bottom_frame.pack(side=tk.TOP, expand=1, fill=tk.X) 73 | 74 | self.find_entry.focus_force() 75 | 76 | self.protocol("WM_DELETE_WINDOW", self.on_cancel) 77 | 78 | def on_find(self): 79 | self.master.find(self.text_to_find.get()) 80 | 81 | def on_replace(self): 82 | self.master.replace_text(self.text_to_find.get(), self.text_to_replace_with.get()) 83 | 84 | def on_cancel(self): 85 | self.master.cancel_find() 86 | self.destroy() 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/quiet_linenumbers.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | 3 | class TextLineNumbers(tk.Canvas): 4 | def __init__(self, parent, *args, **kwargs): 5 | tk.Canvas.__init__(self, *args, **kwargs) 6 | self._text_font = parent.settings['font_family'] 7 | self._parent = parent 8 | self.textwidget = parent.textarea 9 | 10 | def attach(self, text_widget): 11 | self.textwidget = text_widget 12 | 13 | def redraw(self, *args): 14 | font_color = self._parent.menu_fg 15 | bg_color = self._parent.bg_color 16 | indicator_on = self._parent.current_line_indicator 17 | current_line_symbol = self._parent.current_line_symbol 18 | if not self.visible: 19 | return 20 | 21 | self.delete('all') 22 | self.config(width=(self._parent.font_size * 3), 23 | bd=0, bg=bg_color, highlightthickness=0) 24 | 25 | i = self.textwidget.index('@0,0') 26 | while True: 27 | dline= self.textwidget.dlineinfo(i) 28 | if dline is None: break 29 | y = dline[1] 30 | index = self.textwidget.index(tk.INSERT) 31 | pos = index.split('.')[0] 32 | if float(i) >= 10: 33 | linenum = str(i).split('.')[0] 34 | if pos == linenum and indicator_on: 35 | linenum = linenum + current_line_symbol 36 | else: 37 | linenum = '~' + str(i).split('.')[0] 38 | if '~' + pos == linenum and indicator_on: 39 | linenum = linenum + current_line_symbol 40 | self.create_text(2, y, anchor='nw', 41 | text=linenum, 42 | font=(self._text_font, self._parent.font_size), 43 | fill=font_color) 44 | i = self.textwidget.index('%s+1line' % i) 45 | 46 | @property 47 | def visible(self): 48 | return self.cget('state') == 'normal' 49 | 50 | @visible.setter 51 | def visible(self, visible): 52 | self.config(state='normal' if visible else 'disabled') 53 | 54 | if visible: 55 | self.redraw() 56 | else: 57 | self.delete('all') 58 | self.config(width=0) 59 | -------------------------------------------------------------------------------- /src/quiet_loaders.py: -------------------------------------------------------------------------------- 1 | from yaml import load, dump, FullLoader 2 | import sys, os 3 | 4 | class QuietLoaders: 5 | 6 | def resource_path(self, relative): 7 | if hasattr(sys, "_MEIPASS"): 8 | return os.path.join(sys._MEIPASS, relative) 9 | return os.path.join(relative) 10 | 11 | def __init__(self): 12 | self.settings_path = self.resource_path(os.path.join('data', 'config/settings.yaml')) 13 | self.default_settings_path = self.resource_path(os.path.join('data', 'config/settings-default.yaml')) 14 | 15 | def load_settings_data(self, default=False): 16 | if not default: 17 | with open(self.settings_path, 'r') as some_config: 18 | return load(some_config, Loader=FullLoader) 19 | else: 20 | with open(self.default_settings_path, 'r') as some_config: 21 | return load(some_config, Loader=FullLoader) 22 | 23 | def store_settings_data(self, new_settings): 24 | with open(self.settings_path, 'w') as settings_config: 25 | dump(new_settings, settings_config) 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/quiet_main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tkinter as tk 3 | import tkinter.font as tk_font 4 | import re 5 | import sys 6 | from platform import system 7 | from tkinter import (filedialog, ttk) 8 | from quiet_syntax_highlighting import SyntaxHighlighting 9 | from quiet_menubar import Menubar 10 | from quiet_statusbar import Statusbar 11 | from quiet_linenumbers import TextLineNumbers 12 | from quiet_textarea import CustomText 13 | from quiet_find import FindWindow 14 | from quiet_context import ContextMenu 15 | from quiet_loaders import QuietLoaders 16 | from quiet_tree import FileTree 17 | 18 | class QuietText(tk.Frame): 19 | def __init__(self, *args, **kwargs): 20 | """The main class for bringing the whole shebang together""" 21 | tk.Frame.__init__(self, *args, **kwargs) 22 | master.title('untitled - Quiet Text') 23 | # defined size of the editer window 24 | master.geometry('1280x720') 25 | self.configure(bg='black') 26 | self.loader = QuietLoaders() 27 | self.operating_system = system() 28 | self.quiet_icon_path = self.loader.resource_path(os.path.join('data', 'q.png')) 29 | self.icon = tk.PhotoImage(file = self.quiet_icon_path) 30 | master.iconphoto(False, self.icon) 31 | 32 | # start editor according to defined settings in settings.yaml 33 | self.settings = self.loader.load_settings_data() 34 | 35 | #editable settings variables 36 | self.browser = self.settings['web_browser'] 37 | self.font_family = self.settings['font_family'] 38 | self.tab_size = self.settings['tab_size'] 39 | self.font_size = int(self.settings['font_size']) 40 | self.top_spacing = self.settings['text_top_lineheight'] 41 | self.bottom_spacing = self.settings['text_bottom_lineheight'] 42 | self.padding_x = self.settings['textarea_padding_x'] 43 | self.padding_y = self.settings['textarea_padding_y'] 44 | self.insertion_blink = 300 if self.settings['insertion_blink'] else 0 45 | self.tab_size_spaces = self.settings['tab_size'] 46 | self.text_wrap = self.settings['text_wrap'] 47 | self.autoclose_parentheses = self.settings['autoclose_parentheses'] 48 | self.autoclose_curlybraces = self.settings['autoclose_curlybraces'] 49 | self.autoclose_squarebrackets = self.settings['autoclose_squarebrackets'] 50 | self.autoclose_singlequotes = self.settings['autoclose_singlequotes'] 51 | self.autoclose_doublequotes = self.settings['autoclose_doublequotes'] 52 | self.scrollx_width = self.settings['horizontal_scrollbar_width'] 53 | self.scrolly_width = self.settings['vertical_scrollbar_width'] 54 | self.current_line_symbol = self.settings['current_line_indicator_symbol'] 55 | self.current_line_indicator = self.settings['current_line_indicator'] 56 | self.border = self.settings['textarea_border'] 57 | 58 | # Editor color scheme variables 59 | self.insertion_color = '#eb4034' 60 | self.bg_color = '#eb4034' 61 | self.font_color = '#eb4034' 62 | self.text_selection_bg_clr = '#eb4034' 63 | self.scrollx_clr = '#242222' 64 | self.troughx_clr = '#242222' 65 | self.scrollx_active_bg = '#423d3d' 66 | self.scrolly_clr = '#242222' 67 | self.troughy_clr = '#242222' 68 | self.scrolly_active_bg = '#423d3d' 69 | self.menubar_bg_active = '#eb4034' 70 | self.menubar_fg_active = '#eb4034' 71 | self.menu_fg = '#eb4034' 72 | self.menu_bg = '#eb4034' 73 | 74 | #configuration of the file dialog text colors. 75 | self.font_style = tk_font.Font(family=self.font_family, 76 | size=self.font_size) 77 | 78 | self.italics = tk_font.Font(family=self.font_family, slant='italic', size=self.font_size) 79 | self.bold = tk_font.Font(family=self.font_family, weight='bold', size=self.font_size) 80 | self.header1 = tk_font.Font(family=self.font_family, weight='bold', size=self.font_size + 15) 81 | self.header2 = tk_font.Font(family=self.font_family, weight='bold', size=self.font_size + 7) 82 | 83 | self.master = master 84 | self.filename = os.getcwd() 85 | self.dirname = os.getcwd() 86 | 87 | self.previous_file = None 88 | self.textarea = CustomText(self) 89 | 90 | self.scrolly = tk.Scrollbar( 91 | master, 92 | command=self.textarea.yview, 93 | bg=self.scrolly_clr, 94 | troughcolor=self.troughy_clr, 95 | bd=0, 96 | width=self.scrolly_width, 97 | highlightthickness=0, 98 | activebackground=self.scrolly_active_bg, 99 | orient='vertical') 100 | 101 | self.scrollx = tk.Scrollbar( 102 | master, 103 | command=self.textarea.xview, 104 | bg=self.scrollx_clr, 105 | troughcolor=self.troughx_clr, 106 | bd=0, 107 | width=self.scrollx_width, 108 | highlightthickness=0, 109 | activebackground=self.scrollx_active_bg, 110 | orient='horizontal') 111 | 112 | self.textarea.configure( 113 | yscrollcommand=self.scrolly.set, 114 | xscrollcommand=self.scrollx.set, 115 | bg=self.bg_color, 116 | fg=self.font_color, 117 | wrap= self.text_wrap, 118 | spacing1=self.top_spacing, 119 | spacing3=self.bottom_spacing, 120 | selectbackground= self.text_selection_bg_clr, 121 | insertbackground=self.insertion_color, 122 | insertofftime=self.insertion_blink, 123 | bd=self.border, 124 | highlightthickness=self.border, 125 | highlightbackground='black', 126 | font=self.font_style, 127 | undo=True, 128 | autoseparators=True, 129 | maxundo=-1, 130 | padx=self.padding_x, 131 | pady=self.padding_y) 132 | 133 | self.initial_content = self.textarea.get("1.0", tk.END) 134 | 135 | #retrieving the font from the text area and setting a tab width 136 | self._font = tk_font.Font(font=self.textarea['font']) 137 | self._tab_width = self._font.measure(' ' * self.tab_size_spaces) 138 | self.textarea.config(tabs=(self._tab_width,)) 139 | 140 | self.context_menu = ContextMenu(self) 141 | self.linenumbers = TextLineNumbers(self) 142 | self.syntax_highlighter = SyntaxHighlighting(self, self.textarea, self.initial_content) 143 | self.menubar = Menubar(self) 144 | self.statusbar = Statusbar(self) 145 | self.syntax_highlighter.syntax_and_themes.load_theme_from_config() 146 | 147 | self.linenumbers.attach(self.textarea) 148 | self.scrolly.pack(side=tk.RIGHT, fill=tk.Y) 149 | self.scrollx.pack(side=tk.BOTTOM, fill=tk.X) 150 | self.linenumbers.pack(side=tk.LEFT, fill=tk.Y) 151 | self.textarea.pack(side=tk.RIGHT, fill='both', expand=True) 152 | 153 | self.textarea.find_match_index = None 154 | self.textarea.find_search_starting_index = 1.0 155 | 156 | #calling function to bind hotkeys. 157 | self.bind_shortcuts() 158 | self.control_key = False 159 | self.menu_hidden = False 160 | self.first_word = True 161 | 162 | def clear_and_replace_textarea(self): 163 | self.textarea.delete(1.0, tk.END) 164 | try: 165 | if self.filename: 166 | with open(self.filename, 'r') as f: 167 | self.textarea.insert(1.0, f.read()) 168 | self.syntax_highlighter.initial_highlight() 169 | except TypeError as e: 170 | print(e) 171 | 172 | #reconfigure the tab_width depending on changes. 173 | def set_new_tab_width(self, tab_spaces = 'default'): 174 | if tab_spaces == 'default': 175 | space_count = self.tab_size_spaces 176 | else: 177 | space_count = tab_spaces 178 | _font = tk_font.Font(font=self.textarea['font']) 179 | _tab_width = _font.measure(' ' * int(space_count)) 180 | self.textarea.config(tabs=(_tab_width,)) 181 | 182 | # editor basic settings can be altered here 183 | #function used to reload settings after the user changes in settings.yaml 184 | def reconfigure_settings(self, overwrite_with_default=False): 185 | if overwrite_with_default: 186 | _settings = self.loader.load_settings_data(default=True) 187 | else: 188 | _settings = self.loader.load_settings_data() 189 | font_family = _settings['font_family'] 190 | top_spacing = _settings['text_top_lineheight'] 191 | bottom_spacing = _settings['text_bottom_lineheight'] 192 | insertion_blink = 300 if _settings['insertion_blink'] else 0 193 | tab_size_spaces = _settings['tab_size'] 194 | padding_x = _settings['textarea_padding_x'] 195 | padding_y = _settings['textarea_padding_y'] 196 | text_wrap = _settings['text_wrap'] 197 | border = _settings['textarea_border'] 198 | scrollx_width = _settings['horizontal_scrollbar_width'] 199 | scrolly_width = _settings['vertical_scrollbar_width'] 200 | self.autoclose_parentheses = _settings['autoclose_parentheses'] 201 | self.autoclose_curlybraces = _settings['autoclose_curlybraces'] 202 | self.autoclose_squarebrackets = _settings['autoclose_squarebrackets'] 203 | self.autoclose_singlequotes = _settings['autoclose_singlequotes'] 204 | self.autoclose_doublequotes = _settings['autoclose_doublequotes'] 205 | self.linenumbers.current_line_symbol = _settings['current_line_indicator_symbol'] 206 | self.linenumbers.indicator_on = _settings['current_line_indicator'] 207 | self.browser = _settings['web_browser'] 208 | self.textarea.reload_text_settings() 209 | self.set_new_tab_width(tab_size_spaces) 210 | self.menubar.reconfigure_settings() 211 | self.linenumbers._text_font = font_family 212 | self.linenumbers.redraw() 213 | 214 | font_style = tk_font.Font(family=font_family, 215 | size=_settings['font_size']) 216 | 217 | self.menubar._menubar.configure( 218 | fg=self.menu_fg, 219 | bg=self.menu_bg, 220 | activeforeground=self.menubar_fg_active, 221 | activebackground=self.menubar_bg_active, 222 | activeborderwidth=0, 223 | bd=0) 224 | 225 | self.context_menu.right_click_menu.configure( 226 | font=font_family, 227 | fg=self.menu_fg, 228 | bg=self.bg_color, 229 | activebackground=self.menubar_bg_active, 230 | activeforeground=self.menubar_fg_active, 231 | bd=0, 232 | tearoff=0) 233 | 234 | self.scrolly.configure( 235 | bg=self.scrolly_clr, 236 | troughcolor=self.troughy_clr, 237 | width=scrolly_width, 238 | activebackground=self.scrolly_active_bg) 239 | 240 | self.scrollx.configure( 241 | bg=self.scrollx_clr, 242 | troughcolor=self.troughx_clr, 243 | width=scrollx_width, 244 | activebackground=self.scrolly_active_bg) 245 | 246 | self.textarea.configure( 247 | font=font_style, 248 | bg=self.bg_color, 249 | pady=padding_y, 250 | padx=padding_x, 251 | fg=self.font_color, 252 | spacing1=top_spacing, 253 | spacing3=bottom_spacing, 254 | insertbackground=self.insertion_color, 255 | selectbackground= self.text_selection_bg_clr, 256 | insertofftime=insertion_blink, 257 | bd=border, 258 | highlightthickness=border, 259 | wrap=text_wrap) 260 | 261 | 262 | if overwrite_with_default: 263 | MsgBox = tk.messagebox.askquestion( 264 | 'Reset Settings?', 265 | 'Are you sure you want to reset the editor settings to their default value?', 266 | icon='warning') 267 | if MsgBox == 'yes': 268 | self.loader.store_settings_data(_settings) 269 | else: 270 | if self.filename == self.loader.settings_path: 271 | self.save(self.loader.settings_path) 272 | self.reconfigure_settings() 273 | 274 | # editor quiet mode calling which removes status bar and menu bar 275 | def enter_quiet_mode(self, *args): 276 | self.statusbar.hide_status_bar() 277 | self.menubar.hide_menu() 278 | self.scrollx.configure(width=0) 279 | self.scrolly.configure(width=0) 280 | self.statusbar.update_status('quiet') 281 | 282 | # editor leaving quite enu to bring back status bar and menu bar 283 | def leave_quiet_mode(self, *args): 284 | self.statusbar.show_status_bar() 285 | self.menubar.show_menu() 286 | self.scrollx.configure(width=8) 287 | self.scrolly.configure(width=8) 288 | self.statusbar.update_status('hide') 289 | 290 | #hide status bar for text class so it can be used in menu class 291 | def hide_status_bar(self, *args): 292 | self.statusbar.hide_status_bar() 293 | 294 | # toggle the visibility of line numbers 295 | def toggle_linenumbers(self, *args): 296 | self.linenumbers.visible = not self.linenumbers.visible 297 | 298 | # setting up the editor title 299 | #Renames the window title bar to the name of the current file. 300 | def set_window_title(self, name=None): 301 | if name: 302 | self.master.title(f'{name} - Quiet Text') 303 | else: 304 | self.master.title('untitled - Quiet Text') 305 | 306 | 307 | def load_previous_file(self, *args): 308 | if self.previous_file: 309 | try: 310 | previous = self.filename 311 | self.filename = self.previous_file 312 | self.previous_file = previous 313 | self.set_window_title(name=self.filename) 314 | self.initialize_syntax() 315 | self.clear_and_replace_textarea() 316 | except PermissionError as e: 317 | print(e) 318 | 319 | # new file creating in the editor feature 320 | #Deletes all of the text in the current area and sets window title to default. 321 | def new_file(self, *args): 322 | self.textarea.delete(1.0, tk.END) 323 | try: 324 | new_file = filedialog.asksaveasfilename( 325 | parent=self.master, 326 | title='New', 327 | initialdir=self.dirname, 328 | initialfile='untitled', 329 | filetypes=[('All Files', '*.*'), 330 | ('Text Files', '*.txt'), 331 | ('Python Scripts', '*.py'), 332 | ('Python No Terminal Scripts', '*.py'), 333 | ('Markdown Documents', '*.md'), 334 | ('JavaScript Files', '*.js'), 335 | ('Java Files', '*.java'), 336 | ('Haskell Files', '*.hs'), 337 | ('HTML Documents', '*.js'), 338 | ('CSS Documents', '*.css'), 339 | ('C Files', '*.c'), 340 | ('C++ Files', '*.cpp'), 341 | ('CoffeeScript Files', '*.coffee'), 342 | ('Dart Files', '*.dart'), 343 | ('Go Files', '*.go'), 344 | ('Rust Files', '*.rs'), 345 | ('Swift Files', '*.swift')]) 346 | self.previous_file = self.filename 347 | self.filename = new_file 348 | textarea_content = self.textarea.get(1.0, tk.END) 349 | self.set_window_title('untitled') 350 | with open(new_file, 'w') as f: 351 | f.write(textarea_content) 352 | self.set_window_title(self.filename) 353 | self.statusbar.update_status('created') 354 | self.initialize_syntax() 355 | except Exception as e: 356 | print(e) 357 | 358 | def initialize_syntax(self): 359 | if self.filename: 360 | self.clear_and_replace_textarea() 361 | if self.filename[-4:] == '.txt' or self.filename[-3:] == '.md': 362 | self.syntax_highlighter.syntax_and_themes.load_markdown_syntax() 363 | elif self.filename[-2:] == '.c': 364 | self.syntax_highlighter.syntax_and_themes.load_c_syntax() 365 | elif self.filename[-2:] == '.coffee': 366 | self.syntax_highlighter.syntax_and_themes.load_coffeescript_syntax() 367 | elif self.filename[-5:] == '.dart': 368 | self.syntax_highlighter.syntax_and_themes.load_dart_syntax() 369 | elif self.filename[-3:] == '.py': 370 | self.syntax_highlighter.syntax_and_themes.load_python3_syntax() 371 | elif self.filename[-4:] == '.pyw': 372 | self.syntax_highlighter.syntax_and_themes.load_python3_syntax() 373 | elif self.filename[-3:] == '.js': 374 | self.syntax_highlighter.syntax_and_themes.load_javascript_syntax() 375 | elif self.filename[-5:] == '.java': 376 | self.syntax_highlighter.syntax_and_themes.load_java_syntax() 377 | elif self.filename[-3:] == '.hs': 378 | self.syntax_highlighter.syntax_and_themes.load_haskell_syntax() 379 | elif self.filename[-5:] == '.html': 380 | self.syntax_highlighter.syntax_and_themes.load_html_syntax() 381 | elif self.filename[-4:] == '.css': 382 | self.syntax_highlighter.syntax_and_themes.load_css_syntax() 383 | elif self.filename[-4:] == '.cpp': 384 | self.syntax_highlighter.syntax_and_themes.load_cpp_syntax() 385 | elif self.filename[-3:] == '.go': 386 | self.syntax_highlighter.syntax_and_themes.load_go_syntax() 387 | elif self.filename[-3:] == '.rs': 388 | self.syntax_highlighter.syntax_and_themes.load_rust_syntax() 389 | elif self.filename[-4:] == '.sql': 390 | self.syntax_highlighter.syntax_and_themes.load_sql_syntax() 391 | elif self.filename[-5:] == '.yaml': 392 | self.syntax_highlighter.syntax_and_themes.load_yaml_syntax() 393 | elif self.filename[-6:] == '.swift': 394 | self.syntax_highlighter.syntax_and_themes.load_swift_syntax() 395 | elif self.filename[-10:] == 'Dockerfile': 396 | self.syntax_highlighter.syntax_and_themes.load_docker_syntax() 397 | elif self.filename[-4:] == '.nim': 398 | self.syntax_highlighter.syntax_and_themes.load_nim_syntax() 399 | 400 | # opening an existing file in the editor 401 | def open_file(self, *args): 402 | # various file types that editor can support 403 | self.previous_file = self.filename 404 | try: 405 | self.filename = filedialog.askopenfilename( 406 | parent=self.master, 407 | initialdir=self.dirname, 408 | filetypes=[('All Files', '*.*'), 409 | ('Text Files', '*.txt'), 410 | ('Python Scripts', '*.py'), 411 | ('Python No Terminal Scripts', '*.py'), 412 | ('Markdown Documents', '*.md'), 413 | ('JavaScript Files', '*.js'), 414 | ('Java Files', '*.java'), 415 | ('Haskell Files', '*.hs'), 416 | ('HTML Documents', '*.html'), 417 | ('CSS Documents', '*.css'), 418 | ('C Files', '*.c'), 419 | ('C++ Files', '*.cpp'), 420 | ('CoffeeScript Files', '*.coffee'), 421 | ('Dart Files', '*.dart'), 422 | ('Go Files', '*.go'), 423 | ('Rust Files', '*.rs'), 424 | ('Sql Files', '*.sql'), 425 | ('Swift Files', '*.swift')]) 426 | 427 | self.initialize_syntax() 428 | if not self.filename: 429 | self.filename = self.previous_file 430 | self.set_window_title(name=self.filename) 431 | except Exception as e: 432 | print(e) 433 | 434 | def open_dir(self): 435 | try: 436 | self.dirname = filedialog.askdirectory( 437 | parent=self.master) 438 | self.filename = self.dirname 439 | if self.dirname: 440 | self.set_window_title(name=self.filename) 441 | FileTree(self) 442 | except Exception: 443 | pass 444 | 445 | def show_file_tree(self, *args): 446 | self.control_key = False 447 | self.textarea.isControlPressed = False 448 | FileTree(self) 449 | return 'break' 450 | 451 | # saving changes made in the file 452 | def save(self,*args): 453 | if self.filename: 454 | try: 455 | textarea_content = self.textarea.get(1.0, tk.END) 456 | with open(self.filename, 'w') as f: 457 | f.write(textarea_content) 458 | self.statusbar.update_status('saved') 459 | if self.filename == self.loader.settings_path: 460 | self.reconfigure_settings() 461 | self.menubar.reconfigure_settings() 462 | except Exception as e: 463 | pass 464 | else: 465 | self.save_as() 466 | 467 | # saving file as a particular name 468 | def save_as(self, *args): 469 | try: 470 | self.filename = filedialog.asksaveasfilename( 471 | parent=self.master, 472 | initialdir=self.dirname, 473 | filetypes=[('All Files', '*.*'), 474 | ('Text Files', '*.txt'), 475 | ('Python Scripts', '*.py'), 476 | ('Python No Terminal Scripts', '*.pyw'), 477 | ('Markdown Documents', '*.md'), 478 | ('Javascript Files', '*.js'), 479 | ('Java Files', '*.java'), 480 | ('Haskell Files', '*.hs'), 481 | ('HTML Documents', '*.html'), 482 | ('CSS Documents', '*.css'), 483 | ('C Files', '*.c'), 484 | ('C++ Files', '*.cpp'), 485 | ('CoffeeScript Files', '*.coffee'), 486 | ('Dart Files', '*.dart'), 487 | ('Go Files', '*.go'), 488 | ('Rust Files', '*.rs'), 489 | ('Sql Files', '*.sql'), 490 | ('Swift Files', '*.swift')]) 491 | 492 | textarea_content = self.textarea.get(1.0, tk.END) 493 | with open(self.filename, 'w') as f: 494 | f.write(textarea_content) 495 | self.filename = new_file 496 | self.set_window_title(self.filename) 497 | self.statusbar.update_status('saved') 498 | self.initialize_syntax() 499 | except Exception as e: 500 | pass 501 | 502 | #On exiting the Program 503 | def quit_save(self): 504 | try: 505 | os.path.isfile(self.filename) 506 | self.save() 507 | except Exception: 508 | self.save_as() 509 | sys.exit() 510 | 511 | def on_closing(self): 512 | message = tk.messagebox.askyesnocancel("Save On Close", "Do you want to save the changes before closing?") 513 | if message == True: 514 | self.quit_save() 515 | elif message == False: 516 | sys.exit() 517 | else: 518 | return 519 | 520 | # opens the main setting file of the editor 521 | def open_settings_file(self): 522 | self.syntax_highlighter.syntax_and_themes.load_yaml_syntax() 523 | self.previous_file = self.filename 524 | self.filename = self.loader.settings_path 525 | self.textarea.delete(1.0, tk.END) 526 | with open(self.filename, 'r') as f: 527 | self.textarea.insert(1.0, f.read()) 528 | self.syntax_highlighter.initial_highlight() 529 | self.set_window_title(name=self.filename) 530 | 531 | # reset the settings set by the user to the default settings 532 | def reset_settings_file(self): 533 | self.reconfigure_settings(overwrite_with_default=True) 534 | self.syntax_highlighter.syntax_and_themes.load_material() 535 | try: 536 | self.clear_and_replace_textarea() 537 | except IsADirectoryError: 538 | pass 539 | 540 | # select all written text in the editor 541 | def select_all_text(self, *args): 542 | self.textarea.tag_add(tk.SEL, '1.0', tk.END) 543 | self.textarea.mark_set(tk.INSERT, '1.0') 544 | self.textarea.see(tk.INSERT) 545 | return 'break' 546 | 547 | # give hex colors to the file content for better understanding 548 | def apply_hex_color(self, key_event): 549 | new_color = self.menubar.open_color_picker() 550 | try: 551 | sel_start = self.textarea.index(tk.SEL_FIRST) 552 | sel_end = self.textarea.index(tk.SEL_LAST) 553 | self.textarea.delete(sel_start, sel_end) 554 | self.textarea.insert(sel_start, new_color) 555 | except tk.TclError: 556 | pass 557 | 558 | def _on_change(self, key_event): 559 | self.linenumbers.redraw() 560 | 561 | def _on_mousewheel(self, event): 562 | if self.control_key: 563 | self.change_font_size(1 if event.delta > 0 else -1) 564 | 565 | def _on_linux_scroll_up(self, _): 566 | if self.control_key: 567 | self.change_font_size(1) 568 | if self.filename == self.loader.settings_path: 569 | self.syntax_highlighter.initial_highlight() 570 | 571 | def _on_linux_scroll_down(self, _): 572 | if self.control_key: 573 | self.change_font_size(-1) 574 | if self.filename == self.loader.settings_path: 575 | self.syntax_highlighter.initial_highlight() 576 | 577 | def change_font_size(self, delta): 578 | self.font_size = self.font_size + delta 579 | min_font_size = 6 580 | self.font_size = min_font_size if self.font_size < min_font_size else self.font_size 581 | 582 | self.font_style = tk_font.Font(family=self.font_family, 583 | size=self.font_size) 584 | 585 | self.italics = tk_font.Font(family=self.font_family, 586 | size=self.font_size, 587 | slant='italic') 588 | 589 | self.bold = tk_font.Font(family=self.font_family, 590 | size = self.font_size, 591 | weight='bold') 592 | 593 | self.header1 = tk_font.Font(family=self.font_family, 594 | size = self.font_size + 15, 595 | weight='bold') 596 | 597 | self.header2 = tk_font.Font(family=self.font_family, 598 | size = self.font_size + 7, 599 | weight='bold') 600 | 601 | self.textarea.configure(font=self.font_style) 602 | self.syntax_highlighter.text.tag_configure("Token.Name.Builtin.Pseudo",font=self.italics) 603 | self.syntax_highlighter.text.tag_configure("Token.Keyword.Type",font=self.italics) 604 | self.syntax_highlighter.text.tag_configure("Token.Keyword.Declaration",font=self.italics) 605 | self.syntax_highlighter.text.tag_configure("Token.Generic.Emph",font=self.italics) 606 | self.syntax_highlighter.text.tag_configure("Token.Generic.Strong",font=self.bold) 607 | self.syntax_highlighter.text.tag_configure("Token.Generic.Heading",font=self.header1) 608 | self.syntax_highlighter.text.tag_configure("Token.Generic.Subheading",font=self.header2) 609 | self.set_new_tab_width() 610 | 611 | _settings = self.loader.load_settings_data() 612 | _settings['font_size'] = self.font_size 613 | self.loader.store_settings_data(_settings) 614 | 615 | if self.filename == self.loader.settings_path: 616 | self.clear_and_replace_textarea() 617 | 618 | 619 | # control_l = 37 620 | # control_r = 109 621 | # mac_control = 262401 #control key in mac keyboard 622 | # mac_control_l = 270336 #tk.LEFT control key in mac os with normal keyboard 623 | # mac_control_r = 262145 #tk.RIGHT control key in mac os with normal keyboard 624 | def _on_keydown(self, event): 625 | if event.keycode in [17, 37, 109, 262401, 270336, 262145]: 626 | self.control_key = True 627 | self.textarea.isControlPressed = True 628 | else: 629 | self.statusbar.update_status('hide') 630 | 631 | def syntax_highlight(self, *args): 632 | if self.first_word: 633 | self.syntax_highlighter.initial_highlight() 634 | self.first_word = not self.first_word 635 | self.syntax_highlighter.default_highlight() 636 | self.control_key = False 637 | self.textarea.isControlPressed = False 638 | 639 | def show_find_window(self, event=None): 640 | self.textarea.tag_configure('find_match', background=self.text_selection_bg_clr) 641 | self.textarea.bg_color = self.bg_color 642 | self.textarea.fg_color = self.menu_fg 643 | self.textarea.active_fg = self.menubar_fg_active 644 | self.textarea.active_bg = self.menubar_bg_active 645 | FindWindow(self.textarea) 646 | self.control_key = False 647 | self.textarea.isControlPressed = False 648 | 649 | def select_all(self): 650 | self.selection_set(0, 'end') 651 | 652 | def autoclose_base(self, symbol): 653 | index = self.textarea.index(tk.INSERT) 654 | self.textarea.insert(index, symbol) 655 | self.textarea.mark_set(tk.INSERT, index) 656 | 657 | def autoclose_parens(self, event): 658 | _, second_char, _, _ = self.get_chars_in_front_and_back() 659 | if self.autoclose_parentheses and not second_char.isalnum(): 660 | self.autoclose_base(')') 661 | 662 | def autoclose_curly_brackets(self, event): 663 | _, second_char, _, _ = self.get_chars_in_front_and_back() 664 | if self.autoclose_curlybraces and not second_char.isalnum(): 665 | self.autoclose_base('}') 666 | 667 | def autoclose_square_brackets(self, event): 668 | _, second_char, _, _ = self.get_chars_in_front_and_back() 669 | if self.autoclose_squarebrackets and not second_char.isalnum(): 670 | self.autoclose_base(']') 671 | 672 | def autoclose_double_quotes(self, event): 673 | _, second_char, _, _ = self.get_chars_in_front_and_back() 674 | if self.autoclose_doublequotes and not second_char.isalnum(): 675 | self.autoclose_base('"') 676 | 677 | def autoclose_single_quotes(self, event): 678 | _, second_char, _, _ = self.get_chars_in_front_and_back() 679 | if self.autoclose_singlequotes and not second_char.isalnum(): 680 | self.autoclose_base("'") 681 | 682 | def get_indent_level(self): 683 | text = self.textarea 684 | line = text.get('insert linestart', 'insert lineend') 685 | match = re.match(r'^(\s+)', line) 686 | current_indent = len(match.group(0)) if match else 0 687 | return current_indent 688 | 689 | def auto_indentation(self): 690 | text = self.textarea 691 | new_indent = self.get_indent_level() 692 | text.insert('insert', '\n' + '\t' * new_indent) 693 | 694 | def auto_block_indentation(self, event): 695 | prev_char, second_char, _, _ = self.get_chars_in_front_and_back() 696 | text = self.textarea 697 | if prev_char == ':': 698 | current_indent = self.get_indent_level() 699 | new_indent = current_indent + 1 700 | text.insert('insert', '\n' + '\t' * new_indent) 701 | return 'break' 702 | elif prev_char in '{[(' and second_char in '}])': 703 | current_indent = self.get_indent_level() 704 | new_indent = current_indent + 1 705 | text.insert('insert', '\n\n') 706 | text.insert('insert', '\t' * current_indent) 707 | index = text.index(tk.INSERT) 708 | text.mark_set('insert', str(round(float(index) - 1, 1))) 709 | text.insert('insert', '\t' * new_indent) 710 | return 'break' 711 | else: 712 | self.auto_indentation() 713 | return 'break' 714 | 715 | def get_chars_in_front_and_back(self): 716 | index = self.textarea.index(tk.INSERT) 717 | first_pos = f'{str(index)}-1c' 718 | end_second_pos = f'{str(index)}+1c' 719 | first_char = self.textarea.get(first_pos, index) 720 | second_char = self.textarea.get(index, end_second_pos) 721 | return (first_char, second_char, index, end_second_pos) 722 | 723 | def backspace_situations(self, event): 724 | first_char, second_char, index, end_second_pos = self.get_chars_in_front_and_back() 725 | 726 | if first_char == "'" and second_char == "'": 727 | self.textarea.delete(index, end_second_pos) 728 | elif first_char == '"' and second_char == '"': 729 | self.textarea.delete(index, end_second_pos) 730 | elif first_char == '(' and second_char == ')': 731 | self.textarea.delete(index, end_second_pos) 732 | elif first_char == '{' and second_char == '}': 733 | self.textarea.delete(index, end_second_pos) 734 | elif first_char == '[' and second_char == ']': 735 | self.textarea.delete(index, end_second_pos) 736 | 737 | def hide_and_unhide_menubar(self, key_event): 738 | if self.menu_hidden: 739 | self.menubar.show_menu() 740 | else: 741 | self.menubar.hide_menu() 742 | self.menu_hidden = not self.menu_hidden 743 | 744 | def tab_text(self, event): 745 | index = self.textarea.index("sel.first linestart") 746 | last = self.textarea.index("sel.last linestart") 747 | 748 | if last != index: 749 | if event.state == 0: 750 | while self.textarea.compare(index,"<=", last): 751 | if len(self.textarea.get(index, 'end')) != 0: 752 | self.textarea.insert(index, '\t') 753 | index = self.textarea.index("%s + 1 line" % index) 754 | else: 755 | while self.textarea.compare(index,"<=", last): 756 | if self.textarea.get(index, 'end')[:1] == "\t": 757 | self.textarea.delete(index) 758 | index = self.textarea.index("%s + 1 line" % index) 759 | else: 760 | if event.state == 0: 761 | index = self.textarea.index(tk.INSERT) 762 | self.textarea.insert(index, '\t') 763 | else: 764 | index = self.textarea.index("insert linestart") 765 | if self.textarea.get(index, 'end')[:1] == "\t": 766 | self.textarea.delete(index) 767 | return "break" 768 | 769 | def bind_shortcuts(self, *args): 770 | text = self.textarea 771 | text.bind('', self.new_file) 772 | text.bind('', self.open_file) 773 | text.bind('', self.save) 774 | text.bind('', self.save_as) 775 | text.bind('', self.context_menu.bold) 776 | text.bind('', self.context_menu.hightlight) 777 | text.bind('', self.select_all_text) 778 | text.bind('', self.apply_hex_color) 779 | text.bind('', self.menubar.run) 780 | text.bind('', self.enter_quiet_mode) 781 | text.bind('', self.show_find_window) 782 | text.bind('', self.load_previous_file) 783 | text.bind('', self.show_file_tree) 784 | text.bind('', self.syntax_highlighter.initial_highlight) 785 | text.bind('', self.syntax_highlighter.initial_highlight) 786 | text.bind('', self.leave_quiet_mode) 787 | text.bind('<>', self._on_change) 788 | text.bind('', self._on_change) 789 | text.bind('', self.context_menu.popup) 790 | text.bind('', self._on_mousewheel) 791 | text.bind('', self._on_linux_scroll_up) 792 | text.bind('', self._on_linux_scroll_down) 793 | text.bind('', self._on_keydown) 794 | text.bind('', self.syntax_highlight) 795 | text.bind('', self.syntax_highlighter.initial_highlight) 796 | text.bind('', self.autoclose_parens) 797 | text.bind('', self.autoclose_square_brackets) 798 | text.bind('', self.autoclose_single_quotes) 799 | text.bind('', self.autoclose_double_quotes) 800 | text.bind('', self.autoclose_curly_brackets) 801 | text.bind('', self.auto_block_indentation) 802 | text.bind('', self.backspace_situations) 803 | text.bind('', self.hide_and_unhide_menubar) 804 | text.bind('', self.toggle_linenumbers) 805 | text.bind('', self.tab_text) 806 | if self.operating_system == 'Windows': 807 | text.bind('', self.tab_text) 808 | else: 809 | text.bind('', self.tab_text) 810 | 811 | if __name__ == '__main__': 812 | master = tk.Tk() 813 | qt = QuietText(master) 814 | qt.pack(side='top', fill='both', expand=True) 815 | master.protocol("WM_DELETE_WINDOW", qt.on_closing) 816 | master.mainloop() 817 | 818 | -------------------------------------------------------------------------------- /src/quiet_menubar.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import os 3 | import re 4 | from tkinter.colorchooser import askcolor 5 | from quiet_syntax_highlighting import SyntaxHighlighting 6 | from time import sleep 7 | 8 | 9 | class Menubar(): 10 | # initialising the menu bar of editor 11 | def __init__(self, parent): 12 | self._parent = parent 13 | self.syntax = parent.syntax_highlighter 14 | self.ptrn = r'[^\/]+$' 15 | font_specs = ('Droid Sans Fallback', 12) 16 | 17 | # setting up basic features in menubar 18 | menubar = tk.Menu( 19 | parent.master, 20 | font=font_specs, 21 | fg=parent.menu_fg, 22 | bg=parent.menu_bg, 23 | activeforeground= parent.menubar_fg_active, 24 | activebackground= parent.menubar_bg_active, 25 | activeborderwidth=0, 26 | bd=0) 27 | 28 | parent.master.config(menu=menubar) 29 | self._menubar = menubar 30 | # adding features file dropdown in menubar 31 | file_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0) 32 | file_dropdown.add_command( 33 | label='Load Previous File', 34 | accelerator='Ctrl+P', 35 | command=parent.load_previous_file) 36 | # new file creation feature 37 | file_dropdown.add_command( 38 | label='New File', 39 | accelerator='Ctrl+N', 40 | command=parent.new_file) 41 | # open file feature 42 | file_dropdown.add_command( 43 | label='Open File', 44 | accelerator='Ctrl+O', 45 | command=parent.open_file) 46 | file_dropdown.add_command( 47 | label='Open Directory', 48 | command=parent.open_dir) 49 | # save file feature 50 | file_dropdown.add_command( 51 | label='Save', 52 | accelerator='Ctrl+S', 53 | command=parent.save) 54 | # Save as feature 55 | file_dropdown.add_command( 56 | label='Save As', 57 | accelerator='Ctrl+Shift+S', 58 | command=parent.save_as) 59 | # exit feature 60 | file_dropdown.add_separator() 61 | file_dropdown.add_command( 62 | label='Exit', 63 | command=parent.on_closing) 64 | 65 | # adding featues to settings dropdown in menubar 66 | # Edit settings feature 67 | settings_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0) 68 | settings_dropdown.add_command( 69 | label='Edit Settings', 70 | command=parent.open_settings_file) 71 | # reset settings feature 72 | settings_dropdown.add_command( 73 | label='Reset Settings to Default', 74 | command=parent.reset_settings_file) 75 | 76 | #view dropdown menu 77 | view_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0) 78 | view_dropdown.add_command( 79 | label='Toggle Menu Bar', 80 | accelerator='Alt', 81 | command=self.hide_menu) 82 | view_dropdown.add_command( 83 | label='Hide Status Bar', 84 | command=parent.hide_status_bar) 85 | view_dropdown.add_command( 86 | label='Toggle Line Numbers', 87 | accelerator='Ctrl+Shift+L', 88 | command=parent.toggle_linenumbers) 89 | view_dropdown.add_command( 90 | label='Toggle Text Border', 91 | command=self.toggle_text_border) 92 | view_dropdown.add_command( 93 | label='Shrink/Enlarge Horizontal Scrollbar', 94 | command=self.toggle_scroll_x) 95 | view_dropdown.add_command( 96 | label='Shrink/Enlarge Vertical Scrollbar', 97 | command=self.toggle_scroll_y) 98 | view_dropdown.add_command( 99 | label='Destroy Horizontal Scrollbar', 100 | command=self._parent.scrollx.forget) 101 | view_dropdown.add_command(label='Destroy Vertical Scrollbar', 102 | command=self._parent.scrolly.forget) 103 | view_dropdown.add_command( 104 | label='Enter Quiet Mode', 105 | accelerator='Ctrl+Q', 106 | command=self.enter_quiet_mode) 107 | 108 | #tools dropdown menu 109 | tools_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0) 110 | tools_dropdown.add_command( 111 | label='Search and Replace', 112 | accelerator='Ctrl+F', 113 | command=parent.show_find_window) 114 | tools_dropdown.add_command( 115 | label='Display File Tree', 116 | accelerator='Ctrl+T', 117 | command=parent.show_file_tree) 118 | tools_dropdown.add_command( 119 | label='Open Color Selector', 120 | accelerator='Ctrl+M', 121 | command=self.open_color_picker) 122 | 123 | #theme dropdown menu 124 | theme_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0) 125 | theme_dropdown.add_command( 126 | label='Dark Heart', 127 | command=self.syntax.syntax_and_themes.load_darkheart) 128 | theme_dropdown.add_command( 129 | label='Dracula', 130 | command=self.syntax.syntax_and_themes.load_dracula) 131 | theme_dropdown.add_command( 132 | label='Desert', 133 | command=self.syntax.syntax_and_themes.load_desert) 134 | theme_dropdown.add_command( 135 | label='Githubly', 136 | command=self.syntax.syntax_and_themes.load_githubly) 137 | theme_dropdown.add_command( 138 | label='Gruvbox', 139 | command=self.syntax.syntax_and_themes.load_gruvbox) 140 | theme_dropdown.add_command( 141 | label='Material', 142 | command=self.syntax.syntax_and_themes.load_material) 143 | theme_dropdown.add_command( 144 | label='Monokai', 145 | command=self.syntax.syntax_and_themes.load_monokai) 146 | theme_dropdown.add_command( 147 | label='Monokai Pro', 148 | command=self.syntax.syntax_and_themes.load_monokai_pro) 149 | theme_dropdown.add_command( 150 | label='Pumpkin', 151 | command=self.syntax.syntax_and_themes.load_pumpkin) 152 | theme_dropdown.add_command( 153 | label='Rust', 154 | command=self.syntax.syntax_and_themes.load_rust) 155 | theme_dropdown.add_command( 156 | label='Solarized', 157 | command=self.syntax.syntax_and_themes.load_solarized) 158 | 159 | syntax_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0) 160 | 161 | syntax_dropdown.add_command( 162 | label='Batch', 163 | command=self.syntax.syntax_and_themes.load_batch_syntax) 164 | syntax_dropdown.add_command( 165 | label='C', 166 | command=self.syntax.syntax_and_themes.load_c_syntax) 167 | syntax_dropdown.add_command( 168 | label='C++', 169 | command=self.syntax.syntax_and_themes.load_cpp_syntax) 170 | syntax_dropdown.add_command( 171 | label="C#", 172 | command=self.syntax.syntax_and_themes.load_csharp_syntax) 173 | syntax_dropdown.add_command( 174 | label='CoffeeScript', 175 | command=self.syntax.syntax_and_themes.load_coffeescript_syntax) 176 | syntax_dropdown.add_command( 177 | label='CSS', 178 | command=self.syntax.syntax_and_themes.load_css_syntax) 179 | syntax_dropdown.add_command( 180 | label='Dart', 181 | command=self.syntax.syntax_and_themes.load_dart_syntax) 182 | syntax_dropdown.add_command( 183 | label='Dockerfile', 184 | command=self.syntax.syntax_and_themes.load_docker_syntax) 185 | syntax_dropdown.add_command( 186 | label='Haskell', 187 | command=self.syntax.syntax_and_themes.load_haskell_syntax) 188 | syntax_dropdown.add_command( 189 | label='HTML/Django', 190 | command=self.syntax.syntax_and_themes.load_html_syntax) 191 | syntax_dropdown.add_command( 192 | label='JavaScript', 193 | command=self.syntax.syntax_and_themes.load_javascript_syntax) 194 | syntax_dropdown.add_command( 195 | label='Java', 196 | command=self.syntax.syntax_and_themes.load_java_syntax) 197 | syntax_dropdown.add_command( 198 | label='Go', 199 | command=self.syntax.syntax_and_themes.load_go_syntax) 200 | syntax_dropdown.add_command( 201 | label='Markdown', 202 | command=self.syntax.syntax_and_themes.load_markdown_syntax) 203 | syntax_dropdown.add_command( 204 | label='Nim', 205 | command=self.syntax.syntax_and_themes.load_nim_syntax) 206 | syntax_dropdown.add_command( 207 | label='Python3', 208 | command=self.syntax.syntax_and_themes.load_python3_syntax) 209 | syntax_dropdown.add_command( 210 | label='Rust', 211 | command=self.syntax.syntax_and_themes.load_rust_syntax) 212 | syntax_dropdown.add_command( 213 | label='SQL', 214 | command=self.syntax.syntax_and_themes.load_sql_syntax) 215 | syntax_dropdown.add_command( 216 | label='Swift', 217 | command=self.syntax.syntax_and_themes.load_swift_syntax) 218 | syntax_dropdown.add_command( 219 | label='Yaml', 220 | command=self.syntax.syntax_and_themes.load_yaml_syntax) 221 | 222 | build_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0) 223 | build_dropdown.add_command( 224 | label='Build', 225 | command=self.build) 226 | build_dropdown.add_command( 227 | label='Run', 228 | accelerator='Ctrl+R', 229 | command=self.run) 230 | build_dropdown.add_command( 231 | label='Build+Run', 232 | command=self.build_run) 233 | 234 | # menubar add buttons 235 | menubar.add_cascade(label='File', menu=file_dropdown) 236 | menubar.add_cascade(label='View', menu=view_dropdown) 237 | menubar.add_cascade(label='Settings', menu=settings_dropdown) 238 | menubar.add_cascade(label='Tools', menu=tools_dropdown) 239 | menubar.add_cascade(label='Syntax', menu=syntax_dropdown) 240 | menubar.add_cascade(label='Themes', menu=theme_dropdown) 241 | menubar.add_cascade(label='Build Options', menu=build_dropdown) 242 | 243 | self.menu_fields = [field for field in ( 244 | file_dropdown, view_dropdown, syntax_dropdown, build_dropdown, 245 | settings_dropdown, tools_dropdown, theme_dropdown)] 246 | 247 | # Settings reconfiguration function 248 | def reconfigure_settings(self): 249 | settings = self._parent.loader.load_settings_data() 250 | for field in self.menu_fields: 251 | field.configure( 252 | bg=self._parent.menu_bg, 253 | fg=self._parent.menu_fg, 254 | activeforeground=self._parent.menubar_fg_active, 255 | activebackground=self._parent.menubar_bg_active, 256 | background = self._parent.bg_color, 257 | ) 258 | 259 | self._menubar.configure( 260 | bg=self._parent.menu_bg, 261 | fg=self._parent.menu_fg, 262 | background = self._parent.bg_color, 263 | activeforeground= self._parent.menubar_fg_active, 264 | activebackground = self._parent.menubar_bg_active, 265 | ) 266 | 267 | # color to different text tye can be set here 268 | def open_color_picker(self): 269 | return askcolor(title='Color Menu', initialcolor='#d5c4a1')[1] 270 | 271 | def toggle_text_border(self): 272 | settings = self._parent.loader.load_settings_data() 273 | border_status = settings['textarea_border'] 274 | if border_status == 0: 275 | self._parent.textarea.configure(bd=0.5) 276 | settings['textarea_border'] = 0.5 277 | elif border_status > 0: 278 | self._parent.textarea.configure(bd=0) 279 | settings['textarea_border'] = 0 280 | self._parent.loader.store_settings_data(settings) 281 | 282 | def toggle_scroll_x(self): 283 | settings = self._parent.loader.load_settings_data() 284 | scrollx_width = settings['horizontal_scrollbar_width'] 285 | if scrollx_width > 0: 286 | self._parent.scrollx.configure(width=0) 287 | settings['horizontal_scrollbar_width'] = 0 288 | elif scrollx_width == 0: 289 | self._parent.scrollx.configure(width=8) 290 | settings['horizontal_scrollbar_width'] = 8 291 | self._parent.loader.store_settings_data(settings) 292 | 293 | def toggle_scroll_y(self): 294 | settings = self._parent.loader.load_settings_data() 295 | scrolly_width = settings['vertical_scrollbar_width'] 296 | if scrolly_width > 0: 297 | self._parent.scrolly.configure(width=0) 298 | settings['vertical_scrollbar_width'] = 0 299 | elif scrolly_width == 0: 300 | self._parent.scrolly.configure(width=8) 301 | settings['vertical_scrollbar_width'] = 8 302 | self._parent.loader.store_settings_data(settings) 303 | 304 | # quiet mode is defined here 305 | def enter_quiet_mode(self): 306 | self._parent.enter_quiet_mode() 307 | 308 | # hiding the menubar 309 | def hide_menu(self): 310 | self._parent.master.config(menu='') 311 | 312 | # display the menubar 313 | def show_menu(self): 314 | self._parent.master.config(menu=self._menubar) 315 | 316 | def base_cmd(self, command): 317 | cmd = None 318 | if self._parent.operating_system == 'Windows': 319 | cmd = f'start cmd.exe @cmd /k {command}' 320 | elif self._parent.operating_system == 'Linux': 321 | cmd = f"gnome-terminal -- bash -c '{command}; read'" 322 | file_from_path = re.search(self.ptrn, self._parent.filename) 323 | filename = file_from_path.group(0) 324 | file_path = self._parent.filename[:-len(filename)] 325 | os.chdir(file_path) 326 | if cmd: 327 | os.system(cmd) 328 | else: 329 | print('cmd went unassigned!') 330 | 331 | def build(self): 332 | try: 333 | file_from_path = re.search(self.ptrn, self._parent.filename) 334 | filename = file_from_path.group(0) 335 | if filename[-3:] == '.go': 336 | self.base_cmd(f'go build {self._parent.filename}') 337 | elif filename[-2:] == '.c': 338 | compiled_name = filename[:-2] 339 | self.base_cmd(f'gcc {filename} -o {compiled_name}') 340 | elif filename[-4:] == '.cpp': 341 | compiled_name = filename[:-4] 342 | self.base_cmd(f'g++ -o {compiled_name} {filename}') 343 | elif filename[-5:] == '.java': 344 | compiled_name = filename[:-5] 345 | self.base_cmd(f'javac {filename}') 346 | elif filename[-3:] == '.rs': 347 | self.base_cmd(f'rustc {filename}') 348 | elif filename[-3:] == '.hs': 349 | compiled_name = filename[:-3] 350 | self.base_cmd(f'ghc -o {compiled_name} {filename}') 351 | else: 352 | self._parent.statusbar.update_status('cant build') 353 | except TypeError: 354 | self._parent.statusbar.update_status('cant build') 355 | 356 | def run(self, *args): 357 | try: 358 | file_from_path = re.search(self.ptrn, self._parent.filename) 359 | filename = file_from_path.group(0) 360 | if filename[-3:] == '.py': 361 | self.base_cmd(f'python {self._parent.filename}') 362 | elif filename[-5:] == '.html': 363 | self.base_cmd(f'{self._parent.browser} {filename}') 364 | elif filename[-3:] == '.js': 365 | self.base_cmd(f'node {self._parent.filename}') 366 | elif filename[-3:] == '.go': 367 | self.base_cmd(f'go run {self._parent.filename}') 368 | elif filename[-2:] == '.c': 369 | compiled_name = filename[:-2] 370 | self.base_cmd(f'{compiled_name}') 371 | elif filename[-4:] == '.cpp': 372 | compiled_name = filename[:-4] 373 | self.base_cmd(f'{compiled_name}') 374 | elif filename[-5:] == '.java': 375 | compiled_name = filename[:-5] 376 | self.base_cmd(f'java {compiled_name}') 377 | elif filename[-3:] == '.rs': 378 | compiled_name = filename[:-3] 379 | self.base_cmd(f'{compiled_name}') 380 | elif filename[-4:] == '.nim': 381 | self.base_cmd(f'nim c -r {filename}') 382 | elif filename[-3:] == '.hs': 383 | compiled_name = filename[:-3] 384 | self.base_cmd(f'{compiled_name}') 385 | else: 386 | self._parent.statusbar.update_status('no python') 387 | except TypeError as e: 388 | print(e) 389 | self._parent.statusbar.update_status('no file run') 390 | 391 | def build_run(self): 392 | self.build() 393 | sleep(.5) 394 | self.run() 395 | -------------------------------------------------------------------------------- /src/quiet_statusbar.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | 3 | class Statusbar: 4 | 5 | # initialising the status bar 6 | def __init__(self, parent): 7 | self._parent = parent 8 | self.save_bg = '#FF6859' 9 | self.status_fg = '#000000' 10 | self.error_bg = '#B00020' 11 | self.hint_bg = '#B15DFF' 12 | # setting up the status bar 13 | font_specs = ('Droid Sans Fallback', 10) 14 | 15 | self.status = tk.StringVar() 16 | 17 | label = tk.Label( 18 | parent.textarea, 19 | textvariable=self.status, 20 | fg=parent.font_color, 21 | bg='#fff', 22 | anchor='se', 23 | font=font_specs) 24 | 25 | self._label = label 26 | 27 | # status update of the status bar 28 | def update_status(self, event): 29 | if event == 'saved': 30 | self.display_status_message('Changes saved', msg_type='save') 31 | elif event == 'no file run': 32 | self.display_status_message('Cannot run. No file selected.') 33 | elif event == 'cant build': 34 | self.display_status_message('Cannot build this type of file.') 35 | elif event == 'no file': 36 | self.display_status_message('No file detected. Create or open a file.') 37 | elif event == 'no python': 38 | self.display_status_message('You cannot run this type of file.') 39 | elif event == 'no txt bold': 40 | self.display_status_message('You can only bold text in text files.') 41 | elif event == 'no txt high': 42 | self.display_status_message('You can only highlight text in text files.') 43 | elif event == 'quiet': 44 | self.display_status_message('You can leave quiet mode by pressing "escape"', msg_type='hint') 45 | elif event == 'created': 46 | self.display_status_message('File created', msg_type='hint') 47 | else: 48 | self.hide_status_bar() 49 | 50 | def display_status_message(self, message, msg_type='error'): 51 | self.show_status_bar() 52 | self.status.set(message) 53 | if msg_type == 'save': 54 | self.save_color() 55 | elif msg_type == 'hint': 56 | self.hint_color() 57 | else: 58 | self.error_color() 59 | 60 | def error_color(self): 61 | self._label.config(bg=self.error_bg, fg=self.status_fg) 62 | 63 | def save_color(self): 64 | self._label.config(bg=self.save_bg, fg=self.status_fg) 65 | 66 | def hint_color(self): 67 | self._label.config(bg=self.hint_bg, fg=self.status_fg) 68 | 69 | # hiding the status bar while in quiet mode 70 | def hide_status_bar(self): 71 | self._label.pack_forget() 72 | 73 | # display of the status bar 74 | def show_status_bar(self): 75 | self._label.pack(side=tk.BOTTOM) 76 | 77 | def reconfigure_status_label(self): 78 | self._label.config() 79 | 80 | -------------------------------------------------------------------------------- /src/quiet_syntax_and_themes.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pygments.lexers import (PythonLexer, RustLexer, CLexer, CppLexer, JavaLexer, MarkdownLexer, CssLexer, 3 | GoLexer, DockerLexer, YamlLexer, JavascriptLexer, HtmlDjangoLexer, SqlLexer, SwiftLexer, 4 | CoffeeScriptLexer, DartLexer, HaskellLexer, NimrodLexer, BatchLexer, CSharpLexer) 5 | 6 | class SyntaxAndThemes: 7 | 8 | def __init__(self, master): 9 | 10 | self.master = master 11 | 12 | self.monokaipro_theme_path = master.parent.loader.resource_path( 13 | os.path.join('data', 'theme_configs/monokai_pro.yaml')) 14 | self.monokai_theme_path = master.parent.loader.resource_path( 15 | os.path.join('data', 'theme_configs/monokai.yaml')) 16 | self.gruvbox_theme_path = master.parent.loader.resource_path( 17 | os.path.join('data', 'theme_configs/gruvbox.yaml')) 18 | self.solarized_theme_path = master.parent.loader.resource_path( 19 | os.path.join('data', 'theme_configs/solarized.yaml')) 20 | self.darkheart_theme_path = master.parent.loader.resource_path( 21 | os.path.join('data', 'theme_configs/dark-heart.yaml')) 22 | self.githubly_theme_path = master.parent.loader.resource_path( 23 | os.path.join('data', 'theme_configs/githubly.yaml')) 24 | self.dracula_theme_path = master.parent.loader.resource_path( 25 | os.path.join('data', 'theme_configs/dracula.yaml')) 26 | self.pumpkin_theme_path = master.parent.loader.resource_path( 27 | os.path.join('data', 'theme_configs/pumpkin.yaml')) 28 | self.material_theme_path = master.parent.loader.resource_path( 29 | os.path.join('data', 'theme_configs/material.yaml')) 30 | self.desert_theme_path = master.parent.loader.resource_path( 31 | os.path.join('data', 'theme_configs/desert.yaml')) 32 | self.rust_theme_path = master.parent.loader.resource_path( 33 | os.path.join('data', 'theme_configs/rust.yaml')) 34 | 35 | self.default_theme_path = self.rust_theme_path 36 | 37 | def load_default(self): 38 | self.master.load_new_theme(self.default_theme_path) 39 | 40 | def load_monokai_pro(self): 41 | self.master.load_new_theme(self.monokaipro_theme_path) 42 | 43 | def load_monokai(self): 44 | self.master.load_new_theme(self.monokai_theme_path) 45 | 46 | def load_gruvbox(self): 47 | self.master.load_new_theme(self.gruvbox_theme_path) 48 | 49 | def load_rust(self): 50 | self.master.load_new_theme(self.rust_theme_path) 51 | 52 | def load_solarized(self): 53 | self.master.load_new_theme(self.solarized_theme_path) 54 | 55 | def load_darkheart(self): 56 | self.master.load_new_theme(self.darkheart_theme_path) 57 | 58 | def load_githubly(self): 59 | self.master.load_new_theme(self.githubly_theme_path) 60 | 61 | def load_dracula(self): 62 | self.master.load_new_theme(self.dracula_theme_path) 63 | 64 | def load_pumpkin(self): 65 | self.master.load_new_theme(self.pumpkin_theme_path) 66 | 67 | def load_material(self): 68 | self.master.load_new_theme(self.material_theme_path) 69 | 70 | def load_desert(self): 71 | self.master.load_new_theme(self.desert_theme_path) 72 | 73 | def load_batch_syntax(self): 74 | self.master.lexer = BatchLexer() 75 | self.master.initial_highlight() 76 | 77 | def load_csharp_syntax(self): 78 | self.master.lexer = CSharpLexer() 79 | self.master.initial_highlight() 80 | 81 | def load_python3_syntax(self): 82 | self.master.lexer = PythonLexer() 83 | self.master.initial_highlight() 84 | 85 | def load_c_syntax(self): 86 | self.master.lexer = CLexer() 87 | self.master.initial_highlight() 88 | 89 | def load_javascript_syntax(self): 90 | self.master.lexer = JavascriptLexer() 91 | self.master.initial_highlight() 92 | 93 | def load_cpp_syntax(self): 94 | self.master.lexer = CppLexer() 95 | self.master.initial_highlight() 96 | 97 | def load_html_syntax(self): 98 | self.master.lexer = HtmlDjangoLexer() 99 | self.master.initial_highlight() 100 | 101 | def load_css_syntax(self): 102 | self.master.lexer = CssLexer() 103 | self.master.initial_highlight() 104 | 105 | def load_go_syntax(self): 106 | self.master.lexer = GoLexer() 107 | self.master.initial_highlight() 108 | 109 | def load_markdown_syntax(self): 110 | self.master.lexer = MarkdownLexer() 111 | self.master.initial_highlight() 112 | 113 | def load_yaml_syntax(self): 114 | self.master.lexer = YamlLexer() 115 | self.master.initial_highlight() 116 | 117 | def load_java_syntax(self): 118 | self.master.lexer = JavaLexer() 119 | self.master.initial_highlight() 120 | 121 | def load_rust_syntax(self): 122 | self.master.lexer = RustLexer() 123 | self.master.initial_highlight() 124 | 125 | def load_docker_syntax(self): 126 | self.master.lexer = DockerLexer() 127 | self.master.initial_highlight() 128 | 129 | def load_sql_syntax(self): 130 | self.master.lexer = SqlLexer() 131 | self.master.initial_highlight() 132 | 133 | def load_coffeescript_syntax(self): 134 | self.master.lexer = CoffeeScriptLexer() 135 | self.master.initial_highlight() 136 | 137 | def load_dart_syntax(self): 138 | self.master.lexer = DartLexer() 139 | self.master.initial_highlight() 140 | 141 | def load_haskell_syntax(self): 142 | self.master.lexer = HaskellLexer() 143 | self.master.initial_highlight() 144 | 145 | def load_swift_syntax(self): 146 | self.master.lexer = SwiftLexer() 147 | self.master.initial_highlight() 148 | 149 | def load_nim_syntax(self): 150 | self.master.lexer = NimrodLexer() 151 | self.master.initial_highlight() 152 | 153 | 154 | # pt: loading themes from settings file 155 | def load_theme_from_config(self): 156 | theme = self.master.parent.loader.load_settings_data()["theme"] 157 | self.master.load_new_theme(theme) 158 | 159 | def save_theme_to_config(self, path): 160 | loader = self.master.parent.loader 161 | data = loader.load_settings_data() 162 | data["theme"] = path 163 | 164 | loader.store_settings_data(data) 165 | -------------------------------------------------------------------------------- /src/quiet_syntax_highlighting.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.font as tk_font 3 | from yaml import load, FullLoader 4 | from pygments import lex 5 | from quiet_syntax_and_themes import SyntaxAndThemes 6 | from pygments.lexers import MarkdownLexer 7 | 8 | class SyntaxHighlighting(): 9 | 10 | def __init__(self, parent, text_widget, initial_content): 11 | self.parent = parent 12 | self.syntax_and_themes = SyntaxAndThemes(self) 13 | self.settings = parent.settings 14 | self.text = text_widget 15 | self.font_family = parent.font_family 16 | self.font_size = parent.font_size 17 | self.previousContent = initial_content 18 | 19 | self.lexer = MarkdownLexer() 20 | self.comment_color = None 21 | self.string_color = None 22 | self.number_color = None 23 | self.keyword_color = None 24 | self.type_color = None 25 | self.operator_color = None 26 | self.bultin_function_color = None 27 | self.class_color = None 28 | self.namespace_color = None 29 | self.class_name_color = None 30 | self.function_name_color = None 31 | self.text_color = None 32 | 33 | def default_highlight(self): 34 | row, _ = self.text.index(tk.INSERT).split('.') 35 | location = f'{row}.0' 36 | content = self.text.get("1.0", "end-1c") 37 | lines = content.split("\n") 38 | if (self.previousContent != content): 39 | self.text.mark_set("range_start", location) 40 | word = str(len(lines[int(row) - 1])) 41 | if int(word) < 10: 42 | data = self.text.get(location, row + ".0" + word) 43 | else: 44 | data = self.text.get(location, row + "." + word) 45 | for token, content in lex(data, self.lexer): 46 | self.text.mark_set("range_end", "range_start + %dc" % len(content)) 47 | self.text.tag_add(str(token), "range_start", "range_end") 48 | self.text.mark_set("range_start", "range_end") 49 | self.previousContent = self.text.get("1.0", "end-1c") 50 | 51 | def initial_highlight(self, *args): 52 | self.clear_existing_tags() 53 | self.text.mark_set("range_start", "1.0") 54 | data = self.text.get("1.0", "end-1c") 55 | for token, content in lex(data, self.lexer): 56 | self.text.mark_set("range_end", "range_start + %dc" % len(content)) 57 | self.text.tag_add(str(token), "range_start", "range_end") 58 | self.text.mark_set("range_start", "range_end") 59 | self.text.tag_configure('Token.Comment.Single', foreground=self.comment_color) 60 | self.text.tag_configure('Token.Comment.Multiline', foreground=self.comment_color) 61 | self.text.tag_configure('Token.Literal.String', foreground=self.string_color) 62 | self.text.tag_configure('Token.Literal.String.Char', foreground=self.string_color) 63 | self.text.tag_configure('Token.Literal.Number.Integer', foreground=self.number_color) 64 | self.text.tag_configure('Token.Literal.Number.Float', foreground=self.number_color) 65 | self.text.tag_configure('Token.Keyword', foreground=self.keyword_color) 66 | self.text.tag_configure('Token.Operator', foreground=self.operator_color) 67 | self.text.tag_configure('Token.Keyword.Type', foreground=self.type_color, font=self.parent.italics) 68 | self.text.tag_configure('Token.Keyword.Declaration', foreground=self.bultin_function_color, font=self.parent.italics) 69 | self.text.tag_configure('Token.Name.Class', foreground=self.class_name_color) 70 | self.text.tag_configure('Token.Text.Whitespace') 71 | self.text.tag_configure('Token.Name.Function', foreground=self.function_name_color) 72 | self.text.tag_configure('Token.Keyword.Namespace', foreground=self.namespace_color) 73 | self.text.tag_configure('Token.Generic.Emph', font=self.parent.italics) 74 | self.text.tag_configure('Token.Generic.Strong', font=self.parent.bold) 75 | self.text.tag_configure('Token.Generic.Heading', font=self.parent.header1) 76 | self.text.tag_configure('Token.Generic.Subheading', font=self.parent.header2) 77 | self.text.tag_configure('Token.Name.Builtin.Pseudo', foreground=self.class_color, font=self.parent.italics) 78 | self.text.tag_configure('Token.Name.Builtin', foreground=self.bultin_function_color) 79 | self.text.tag_configure('Token.Punctuation.Indicator', foreground=self.bultin_function_color) 80 | self.text.tag_configure('Token.Literal.Scalar.Plain', foreground=self.number_color) 81 | self.text.tag_configure('Token.Literal.String.Single', foreground=self.string_color) 82 | self.text.tag_configure('Token.Literal.String.Double', foreground=self.string_color) 83 | self.text.tag_configure('Token.Keyword.Constant', foreground=self.number_color) 84 | self.text.tag_configure('Token.Literal.String.Interpol', foreground=self.string_color) 85 | self.text.tag_configure('Token.Name.Decorator', foreground=self.number_color) 86 | self.text.tag_configure('Token.Operator.Word', foreground=self.operator_color) 87 | self.text.tag_configure('Token.Literal.String.Affix', foreground=self.bultin_function_color) 88 | self.text.tag_configure('Token.Name.Function.Magic', foreground=self.bultin_function_color) 89 | self.text.tag_configure('Token.Literal.Number.Oct', foreground=self.number_color) 90 | self.text.tag_configure('Token.Keyword.Reserved', foreground=self.keyword_color) 91 | self.text.tag_configure('Token.Name.Attribute', foreground=self.bultin_function_color) 92 | self.text.tag_configure('Token.Name.Tag', foreground=self.namespace_color) 93 | self.text.tag_configure('Token.Comment.PreprocFile', forground=self.namespace_color) 94 | self.text.tag_configure('Token.Name.Label', foreground=self.class_color) 95 | self.text.tag_configure('Token.Literal.String.Escape', foreground=self.number_color) 96 | 97 | def load_new_theme(self, path): 98 | with open(path) as new_theme_config: 99 | new_config = load(new_theme_config, Loader=FullLoader) 100 | self.syntax_and_themes.save_theme_to_config(path) 101 | 102 | self.comment_color = new_config['comment_color'] 103 | self.string_color = new_config['string_color'] 104 | self.number_color = new_config['number_color'] 105 | self.keyword_color = new_config['keyword_color'] 106 | self.operator_color = new_config['operator_color'] 107 | self.bultin_function_color = new_config['bultin_function_color'] 108 | self.class_color = new_config['class_self_color'] 109 | self.namespace_color = new_config['namespace_color'] 110 | self.class_name_color = new_config['class_name_color'] 111 | self.function_name_color = new_config['function_name_color'] 112 | self.text_color = new_config['font_color'] 113 | self.type_color = new_config['type_color'] 114 | self.parent.text_selection_bg_clr = new_config['selection_color'] 115 | self.parent.insertion_color = new_config['font_color'] 116 | self.parent.menu_fg = new_config['comment_color'] 117 | self.parent.menu_bg = new_config['bg_color'] 118 | self.parent.font_color = new_config['font_color'] 119 | self.parent.bg_color = new_config['bg_color'] 120 | self.parent.menubar_bg_active = new_config['menu_bg_active'] 121 | self.parent.menubar_fg_active = new_config['menu_fg_active'] 122 | self.menu_bg = new_config['menu_bg_active'] 123 | self.menu_fg = new_config['menu_fg_active'] 124 | self.parent.reconfigure_settings() 125 | self.initial_highlight() 126 | 127 | def clear_existing_tags(self): 128 | for tag in self.text.tag_names(): 129 | self.text.tag_delete(tag) 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/quiet_textarea.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import messagebox 3 | from quiet_loaders import QuietLoaders 4 | 5 | class CustomText(tk.Text): 6 | def __init__(self, *args, **kwargs): 7 | tk.Text.__init__(self, *args, **kwargs) 8 | 9 | # create a proxy for the underlying widget 10 | self.bg_color ='#eb4034' 11 | self.fg_color = '#eb4034' 12 | self.active_fg = '#eb4034' 13 | self.active_bg = '#eb4034' 14 | self.isControlPressed = False 15 | self._orig = self._w + '_orig' 16 | self.tk.call('rename', self._w, self._orig) 17 | self.tk.createcommand(self._w, self._proxy) 18 | 19 | def _proxy(self, *args): 20 | # let the actual widget perform the requested action 21 | try: 22 | cmd = (self._orig,) + args 23 | result = '' 24 | if not self.isControlPressed: 25 | # if command is not present, execute the event 26 | result = self.tk.call(cmd) 27 | else: 28 | # Suppress y-scroll and x-scroll when control is pressed 29 | if args[0:2] not in [('yview', 'scroll'), ('xview', 'scroll')]: 30 | result = self.tk.call(cmd) 31 | except tk.TclError: 32 | result = '' 33 | 34 | # generate an event if something was added or deleted, 35 | # or the cursor position changed 36 | if (args[0] in ('insert', 'replace', 'delete') or 37 | args[0:3] == ('mark', 'set', 'insert') or 38 | args[0:2] == ('xview', 'moveto') or 39 | args[0:2] == ('xview', 'scroll') or 40 | args[0:2] == ('yview', 'moveto') or 41 | args[0:2] == ('yview', 'scroll') 42 | ): 43 | self.event_generate('<>', when='tail') 44 | 45 | # return what the actual widget returned 46 | return result 47 | 48 | def find(self, text_to_find): 49 | length = tk.IntVar() 50 | index = self.search( 51 | text_to_find, 52 | self.find_search_starting_index, 53 | stopindex=tk.END, count=length) 54 | 55 | if index: 56 | self.tag_remove('find_match', 1.0, tk.END) 57 | 58 | end = f'{index}+{length.get()}c' 59 | self.tag_add('find_match', index, end) 60 | self.see(index) 61 | 62 | self.find_search_starting_index = end 63 | self.find_match_index = index 64 | else: 65 | if self.find_match_index != 1.0: 66 | if tk.messagebox.askyesno("No more results", "No further matches. Repeat from the beginning?"): 67 | self.find_search_starting_index = 1.0 68 | self.find_match_index = None 69 | return self.find(text_to_find) 70 | else: 71 | tk.messagebox.showinfo("No Matches", "No matching text found") 72 | 73 | def replace_text(self, target, replacement): 74 | if self.find_match_index: 75 | current_found_index_line = str(self.find_match_index).split('.')[0] 76 | 77 | end = f"{self.find_match_index}+{len(target)}c" 78 | self.replace(self.find_match_index, end, replacement) 79 | 80 | self.find_search_starting_index = current_found_index_line + '.0' 81 | 82 | def cancel_find(self): 83 | self.find_search_starting_index = 1.0 84 | self.find_match_index = None 85 | self.tag_remove('find_match', 1.0, tk.END) 86 | 87 | def reload_text_settings(self): 88 | self.bg_color = '#eb4034' 89 | self.bg_color = '#eb4034' 90 | self.fg_color = '#eb4034' 91 | self.active_fg = '#eb4034' 92 | self.active_bg = '#eb4034' 93 | -------------------------------------------------------------------------------- /src/quiet_tree.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.ttk as ttk 3 | import os 4 | 5 | class FileTree(tk.Toplevel): 6 | def __init__(self, master, **kwargs): 7 | tk.Toplevel.__init__( 8 | self, 9 | bd=0, 10 | bg=master.bg_color, 11 | highlightbackground=master.bg_color) 12 | self.iconphoto(False, master.icon) 13 | self.master = master 14 | self.font_specs = ('Droid Sans Fallback', 12) 15 | self.style = ttk.Style() 16 | self.style.theme_use('classic') 17 | self.style.configure( 18 | 'Treeview', 19 | font=self.font_specs, 20 | foreground=master.menu_fg, 21 | background=master.bg_color, 22 | fieldbackground=master.bg_color, 23 | highlightthickness=0, 24 | bd=0) 25 | self.style.layout("Treeview", [('Treeview.treearea', {'sticky': 'nswe'})]) # Remove the borders 26 | self.geometry('200x400') 27 | self.title('File Tree') 28 | self.transient(master) 29 | self.tree=ttk.Treeview( 30 | self, 31 | height=20, 32 | selectmode='browse', 33 | show='tree', 34 | style='Treeview') 35 | self.minsize(200,125) 36 | self.maxsize(1000, 1000) 37 | 38 | #This somehow kind of works. There is probably a better way to search the file system. 39 | def folder_mania(path, location=""): 40 | try: 41 | starting_content = os.listdir(master.dirname) 42 | except (NotADirectoryError, FileNotFoundError, PermissionError) as e: 43 | print(e) 44 | if path == master.dirname: 45 | location = "" 46 | files = [file for file in starting_content if '.' in file] 47 | folders = [folder for folder in starting_content if '.' not in folder] 48 | else: 49 | new_content = os.listdir(path) 50 | files = [file for file in new_content if '.' in file] 51 | folders = [folder for folder in new_content if '.' not in folder] 52 | summ = 0 53 | for count, folder in enumerate(folders, 1): 54 | if folder: 55 | folder_name=self.tree.insert(location, count, text=folder, values=[folder]) 56 | new_path = path + '/' + folder 57 | try: 58 | if len(os.listdir(new_path)) > 0: 59 | folder_mania(new_path, location=folder_name) 60 | except (NotADirectoryError, FileNotFoundError): 61 | pass 62 | except PermissionError as e: 63 | print(e) 64 | break 65 | for count, file in enumerate(files, 1): 66 | adjusted_count = count + summ 67 | if adjusted_count % 2 == 0: 68 | tag = 'odd' 69 | else: 70 | tag = 'even' 71 | self.tree.insert(location, adjusted_count, text=file, tags=(tag,), values=[location]) 72 | summ += 1 73 | 74 | folder_mania(master.dirname) 75 | self.tree_bindings() 76 | # self.tree.tag_configure('odd', background=self.master.bg_color) 77 | # self.tree.tag_configure('even', background=self.master.menubar_active_bg) 78 | self.tree.pack(side=tk.TOP,fill=tk.BOTH) 79 | 80 | def OnDoubleClick(self, event): 81 | self.master.filename = self.master.dirname 82 | item = self.tree.identify("item", event.x, event.y) 83 | folder_id = self.tree.parent(item) 84 | folder = '' 85 | while True: 86 | try: 87 | folder_path = self.tree.item(folder_id)["values"][0] 88 | folder = folder_path + '/' + folder 89 | folder_id = self.tree.parent(folder_id) 90 | except IndexError: 91 | break 92 | filename = self.tree.item(item)["text"] 93 | file_path = folder + filename if folder else filename 94 | if '.' in file_path: 95 | try: 96 | self.master.filename += '/' + file_path 97 | self.master.initialize_syntax() 98 | except (UnicodeDecodeError, FileNotFoundError, IsADirectoryError): 99 | self.master.filename = os.getcwd() 100 | self.master.set_window_title(self.master.filename) 101 | 102 | 103 | def tree_bindings(self): 104 | self.tree.bind('', self.OnDoubleClick) 105 | 106 | --------------------------------------------------------------------------------