Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add settings menu #37

Merged
merged 24 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6c739d0
feat(sheet): Improve & extend vim cheatsheet
dynobo Feb 18, 2024
993f81d
feat(sheet): Add neovim extension to vscode
dynobo Feb 18, 2024
ff6d2a0
feat(sheet): WIP add forge
dynobo Feb 21, 2024
883939a
feat(config): use fullscreen button instead cli arg
dynobo Feb 21, 2024
b37fd4d
chore(ui): shorten fullscreen bar ids
dynobo Feb 21, 2024
9ab8b99
feat(ui): switch to custom popover menu
dynobo Feb 23, 2024
6c00946
chore(ui): refactor headbar logic; restructure modules
dynobo Feb 25, 2024
50a9819
feat(ui): polish settings menu
dynobo Feb 26, 2024
060c9b8
chore(sheets): rename attributes in toml files
dynobo Mar 10, 2024
c679627
refactor: ui actions; fix type hinting for gtk
dynobo Apr 15, 2024
d66cdef
feat(gui): switch fallback sheet widget; improve menu style
dynobo Apr 17, 2024
7b354cc
refactor: renamings
dynobo Apr 18, 2024
3dcb054
refactor: split utils.py
dynobo Apr 18, 2024
3570381
refactor: simplify debug text
dynobo Apr 18, 2024
0cdd020
refactor: carve out and refactor headerbar; remove comments
dynobo Apr 19, 2024
7463102
feat(gui): improve debug info
dynobo Apr 19, 2024
b9cfa58
chore: comments and naming improvements
dynobo Apr 19, 2024
db5a4b1
fix: typehint broke GTK 4.6 compatibility
dynobo Apr 20, 2024
d9b1efc
fix: exception handling in case of missing gnome extension
dynobo Apr 20, 2024
2379b03
fix: create config dir if missing
dynobo Apr 20, 2024
cfad076
chore: beautify debug info
dynobo Apr 20, 2024
e5006d9
chore: fix linter finding
dynobo Apr 20, 2024
89b2280
chore: update ruff call syntax
dynobo Apr 21, 2024
0d54bf7
feat: verify prerequisites and show banners if missed
dynobo Apr 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# Changelog

## v0.4.4 (2024-02-14)
## v0.5.0 (upcoming)

- Breaking changes:
- Renamed the attribute `regex_process` in `toml`-files to `regex_wmclass`.
- Renamed the attribute `source` in `toml`-files to `url`.
- Removed cli-args which are now covered by settings menu.
- Fix duplicate IDs in included sections.
- Fix error if launched with `--no-section-sort`
- Add settings menu to ui.
- Focus search field on start.
- Move generic CLI commands into separate cheatsheet and include it in terminal apps.
- Cheatsheets:
- Moved some CLI commands into separate cheatsheet and include it in terminal apps.
- Added sheet for Keyhint itself.

## v0.4.3 (2024-02-13)

Expand Down
149 changes: 101 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# KeyHint

**_Utility to display keyboard shortcuts or other hints based on the active window on
Linux. (GTK 4.6+ required!)_**
Linux._**

<p align="center"><br>
<img alt="Tests passing" src="https://github.com/dynobo/keyhint/workflows/Test/badge.svg">
Expand All @@ -24,6 +24,12 @@ Linux. (GTK 4.6+ required!)_**
gobject-introspection \
libgtk-4-dev
```
- Wayland & Gnome: The
[Gnome Extension "Window-Calls"](https://extensions.gnome.org/extension/4724/window-calls/)
is required to auto-select the cheatsheet based on the current active application.
- Wayland & KDE: Not yet implemented.
[Create an issue](https://github.com/dynobo/keyhint/issues/new) if you like to have
this support for this setup.

## Installation

Expand All @@ -35,80 +41,128 @@ Linux. (GTK 4.6+ required!)_**
- Configure a **global hotkey** (e.g. `Ctrl + F1`) **via your system settings** to
launch `keyhint`.
- If KeyHint is launched via hotkey, it detects the current active application and shows
the appropriate hints.
the appropriate hints. (This feature won't work reliably when KeyHint ist started via
Menu or Launcher.)

## CLI Options

```
Application Options:
-c, --cheatsheet=SHEET-ID Show cheatsheet with this ID on startup
-d, --default-cheatsheet=SHEET-ID Cheatsheet to show in case no cheatsheet is found for active application
-f, --no-fullscreen Launch window in normal window state instead of fullscreen mode
-s, --no-section-sort Do not sort sections by size, keep order from config toml file
-o, --orientation=horizontal|vertical Orientation and scroll direction. Default: 'vertical'
-v, --verbose Verbose log output for debugging
```

## Configuration
## Cheatsheet Configuration

- The **config directory** is `~/.config/keyhint/`.
- To **customize existing** cheatsheets, copy
The content which KeyHint displays is configured using [`toml`](https://toml.io/en/)
configuration files.

KeyHint reads those files from two locations:

1. The [built-in directory](https://github.com/dynobo/keyhint/tree/main/keyhint/config)
1. The user directory, usually located in `~/.config/keyhint`

### How Keyhint selects the cheatsheet to show

- The cheatsheet to be displayed on startup are selected by comparing the value of
`regex_wmclass` with the wm_class of the active window and the value of `regex_title`
with the title of the active window.
- The potential cheatsheets are processed alphabetically by filename, the first file
that matches both wm_class and title are getting displayed.
- Both of `regex_` values are interpreted as **case in-sensitive regular expressions**.
- Check "Debug Info" in the application menu to get insights about the active window and
the selected cheatsheet file.

### Customize or add cheatsheets

- To **change built-in** cheatsheets, copy
[the corresponding .toml-file](https://github.com/dynobo/keyhint/tree/main/src/keyhint/config)
into the config directory. Make your changes in a text editor. As long as you don't
change the `id` it will overwrite the defaults.
- To **create new** cheatsheets, I suggest you also start with
- To **create new** cheatsheets, I suggest you start with
[one of the existing .toml-file](https://github.com/dynobo/keyhint/tree/main/src/keyhint/config):
- Place it in the config directory and give it a good file name.
- Change the value `id` to something unique.
- Adjust `regex_process` and `regex_title` so it will be selected based on the active
- Adjust `regex_wmclass` and `regex_title` so it will be selected based on the active
window. (See [Tips](#tips))
- Add the `shortcuts` & `label` to a `section`.
- If you think your cheatsheet might be useful for others, please consider opening a
pull request or an issue.
- You can always **reset a configuration** to the shipped version by deleting the
`.toml` files from the config folder.
- You can **include shortcuts from other cheatsheets** add
pull request or an issue!
- You can always **reset cheatsheets** to the shipped version by deleting the
corresponding `.toml` files from the config folder.
- You can **include shortcuts from other cheatsheets** by adding
`include = ["<Cheatsheet ID>"]`
- You can **hide a cheatsheet** by add `hidden = true` in the top block (same level as
`id` and `title`).

## Tips
### Examples

**Cheatsheet selection:**
#### Hide existing cheatsheets

- The cheatsheet to be displayed on startup are selected by comparing the value of
`regex_process` with the wm_class of the active window and the value of `regex_title`
with the title of the active window.
- The potential cheatsheets are processed alphabetically by filename, the first file
that matches both wm_class and title are getting displayed.
- Both of `regex_` values are interpreted as **case in-sensitive regular expressions**.
- Check "Debug Info" in the application menu to get insights about the active window and
the selected cheatsheet file.
To hide a cheatsheet, e.g. the
[built-in](https://github.com/dynobo/keyhint/blob/main/keyhint/config/tilix.toml) one
with the ID `tilix`, create a new file `~/.config/keyhint/tilix.toml` with the content:

```toml
id = "tilix"
hidden = true
```

**Available cheatsheets:**
#### Extend existing cheatsheets

To add keybindings to an existing cheatsheet, e.g. the
[built-in](https://github.com/dynobo/keyhint/blob/main/keyhint/config/firefox.toml) one
with the ID `firefox`, create a new file `~/.config/keyhint/firefox.toml` which only
contains the ID and the additional bindings:

```toml
id = "firefox"

[section]
[section."My Personal Favorites"] # New section
"Ctrl + Shift + Tab" = "Show all Tabs"
# ...
```

- Check the
[included toml-files](https://github.com/dynobo/keyhint/tree/main/src/keyhint/config)
to see which applications are available by default.
- Feel free submit additional `toml-files` for further applications.
#### Add new cheatsheet which never gets auto-selected

**Differentiate cheatsheets per website:**
To add a new cheatsheet, which never gets automatically selected and displayed by
KeyHint, but remains accessible through KeyHint's cheatsheet dropdown, create a file
`~/.config/keyhint/my-app.toml`:

- For showing different browser-cheatsheets depending on the current website, you might
want to use a browser extension like
"[Add URL To Window Title](https://addons.mozilla.org/en-US/firefox/addon/add-url-to-window-title/)"
and then configure the sections in `<cheatsheet>.toml` to look for the URL in the
window title.
```toml
id = "my-app"
url = "url-to-my-apps-keybindings"

**KeyHint's shortcuts:**
[match]
regex_wmclass = "a^" # Patter which never matches
regex_title = "a^"

- `Ctrl+F`: Start filtering
- `Ctrl+S`: Focus sheet selection dropdown (press `Enter` to open it)
- `Esc`: Exit KeyHint
- `→`, `↓`, `l` or `k`: scroll forward
- `←`, `↑`, `h` or `j`: scroll backward
- `PageDown`: scroll page forward
- `PageUP`: scroll page backward
[section]
[section.General]
"Ctrl + C" = "Copy"
# ...

```

#### Different cheatsheets for different Websites

For showing different browser-cheatsheets depending on the current website, you might
want to use a browser extension like
"[Add URL To Window Title](https://addons.mozilla.org/en-US/firefox/addon/add-url-to-window-title/)"
and configure the `[match]` section to look for the url in the title. E.g.
`~/.config/keyhint/github.toml`

```toml
id = "github.com"

[match]
regex_wmclass = "Firefox"
regex_title = ".*github\\.com.*" # URL added by browser extensions to window title

[section]
[section.Repositories]
gc = "Goto code tab"
# ...
```

## Contribute

Expand All @@ -120,11 +174,10 @@ the
## Design Principles

- **Don't run as service**<br>It shouldn't consume resources in the background, even if
this leads to slower start-up time.
this leads to slightly slower start-up time.
- **No network connection**<br>Everything should run locally without any network
communication.
- **Dependencies**<br>The fewer dependencies, the better.
- **Multi-Monitors**<br>Supports setups with two or more displays

## Certification

Expand Down
3 changes: 0 additions & 3 deletions keyhint/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
from keyhint import utils

__version__ = "0.4.4"
__all__ = ["utils"]
1 change: 1 addition & 0 deletions keyhint/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Package entry for keyhint."""

from keyhint import app

if __name__ == "__main__":
Expand Down
50 changes: 9 additions & 41 deletions keyhint/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,22 @@

import logging
import sys
from collections.abc import Mapping
from typing import Any

import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")

from gi.repository import Adw, Gio, GLib, Gtk # noqa: E402
from gi.repository import Adw, Gio, GLib # noqa: E402

from keyhint.window import KeyhintWindow # noqa: E402

logging.basicConfig(
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
format="%(asctime)s - %(levelname)-7s - %(module)s.py:%(lineno)d - %(message)s",
datefmt="%H:%M:%S",
level="WARNING",
)
logger = logging.getLogger(__name__)
logger = logging.getLogger("keyhint")


class Application(Adw.Application):
Expand All @@ -36,10 +34,12 @@ class Application(Adw.Application):

def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
"""Initialize application with command line options."""
super().__init__(
*args,
kwargs.update(
application_id="eu.dynobo.keyhint",
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
)
super().__init__(
*args,
**kwargs,
)
self.options: dict = {}
Expand All @@ -52,38 +52,6 @@ def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
"Show cheatsheet with this ID on startup",
"SHEET-ID",
)
self.add_main_option(
"default-cheatsheet",
ord("d"),
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
"Cheatsheet to show in case no cheatsheet is found for active application",
"SHEET-ID",
)
self.add_main_option(
"no-fullscreen",
ord("f"),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
"Launch window in normal window state instead of fullscreen mode",
None,
)
self.add_main_option(
"no-section-sort",
ord("s"),
GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
"Do not sort sections by size, keep order from config toml file",
None,
)
self.add_main_option(
"orientation",
ord("o"),
GLib.OptionFlags.NONE,
GLib.OptionArg.STRING,
"Orientation and scroll direction. Default: 'vertical'",
"horizontal|vertical",
)
self.add_main_option(
"verbose",
ord("v"),
Expand All @@ -99,12 +67,12 @@ def do_activate(self, *_, **__) -> None: # noqa: ANN002, ANN003
window.set_application(self)
window.present()

def do_command_line(self, cli: Gtk, **__: Mapping[Any, Any]) -> int:
def do_command_line(self, cli: Gio.ApplicationCommandLine) -> int:
"""Store command line options in class attribute for later usage."""
self.options = cli.get_options_dict().end().unpack()

if "verbose" in self.options:
logging.getLogger().setLevel("DEBUG")
logger.setLevel("DEBUG")
logger.debug("CLI Options: %s", self.options)

self.activate()
Expand Down
Loading
Loading