Skip to content

Commit f217b5e

Browse files
committed
docs: move scattered plan files into PARA-organized docs/
Active plans (TRAY_INTEGRATION, IRCV3_IMPLEMENTATION, WIN32_MESON_MIGRATION, OAUTH_IMPLEMENTATION) go to docs/projects/. Completed migrations (GTK4_LISTVIEW_MIGRATION, GTK4_MIGRATION_PLAN) go to docs/archives/. Fix two intra-doc links in IRCV3_IMPLEMENTATION_PLAN.md that pointed at the old docs/ paths.
1 parent 8b86057 commit f217b5e

13 files changed

Lines changed: 244 additions & 2 deletions

docs/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# PoxChat Documentation
2+
3+
Organized by [PARA](https://fortelabs.com/blog/para/):
4+
5+
- **`projects/`** — active efforts with a goal/endpoint. Each file is a single
6+
initiative being actively worked. Move to `archives/` when the goal is met
7+
or the work is abandoned.
8+
- **`areas/`** — ongoing responsibilities with no end date (e.g. release
9+
process, protocol compatibility tracking, build-system upkeep).
10+
- **`resources/`** — reference material that isn't tied to a specific project
11+
(protocol notes, design rationale, links worth keeping).
12+
- **`archives/`** — inactive items moved from the other three buckets. Kept
13+
for historical context, not for ongoing work.
14+
15+
A file's bucket is determined by whether work is currently being done on it,
16+
not by topic. The same subject can move between `projects/` and `archives/`
17+
as work starts and stops.

docs/archives/.gitkeep

Whitespace-only changes.
File renamed without changes.
File renamed without changes.

docs/areas/.gitkeep

Whitespace-only changes.
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# Input Widget Modernization
2+
3+
`HexInputEdit` (`src/fe-gtk/hex-input-edit.c`, `src/fe-gtk/hex-input-edit.h`)
4+
is a from-scratch GTK4 entry widget built on top of the xtext rendering
5+
pipeline (Pango + Cairo + emoji sprites). It replaces `GtkText`/`GtkEntry` to
6+
gain control over emoji sprites, color codes, and selection appearance — but
7+
in doing so it skips a number of behaviors that `GtkText` provides for free.
8+
9+
This document audits what's missing relative to native entries and tracks the
10+
work to close those gaps.
11+
12+
## Priority ordering (rough)
13+
14+
1. Primary selection + middle-click paste (item 1)
15+
2. Drag-and-drop receive (item 2)
16+
3. Accessibility (item 4)
17+
4. Emoji chooser shortcut (item 5)
18+
5. Everything else
19+
20+
---
21+
22+
## 1. Primary selection sync + middle-click paste
23+
24+
The widget only registers `GtkGestureClick` controllers for
25+
`GDK_BUTTON_PRIMARY` and `GDK_BUTTON_SECONDARY`
26+
(`hex-input-edit.c:2540-2553`). Middle button is unbound. `do_copy()` and
27+
`do_cut()` (`hex-input-edit.c:1030,1045`) write only to the CLIPBOARD
28+
selection via `gdk_display_get_clipboard()` — there is no use of
29+
`gdk_display_get_primary_clipboard()` anywhere in this file. As a result, on
30+
Linux:
31+
32+
- Selecting text inside the input does not populate PRIMARY, so other apps
33+
can't middle-click-paste from us.
34+
- Middle-clicking the input does nothing — no insertion from PRIMARY.
35+
36+
`src/fe-gtk/gtkutil.c:709,717` already uses the primary clipboard elsewhere,
37+
so the pattern is established in the codebase.
38+
39+
- [ ] 1a. Refactor `do_paste()` (`hex-input-edit.c:1062`) to take a
40+
`GdkClipboard *` argument, so it can paste from either CLIPBOARD or
41+
PRIMARY. Existing callers pass `gdk_display_get_clipboard(...)`.
42+
- [ ] 1b. Add a helper `push_selection_to_primary(HexInputEdit *)` that
43+
grabs the current selection text and calls `gdk_clipboard_set_text()`
44+
on the primary clipboard. No-op when there is no selection.
45+
- [ ] 1c. Call the helper from every place the selection changes:
46+
`drag_update_cb`, `click_pressed_cb` n_press==2 (word-select),
47+
`click_pressed_cb` n_press==3 (select-all), `do_select_all`, the
48+
Shift+arrow key paths in `key_pressed_cb`, and the
49+
Shift+Home/Shift+End paths.
50+
- [ ] 1d. Add a third `GtkGestureClick` with
51+
`gtk_gesture_single_set_button(GDK_BUTTON_MIDDLE)`. In its `pressed`
52+
handler: move the cursor to the click position with
53+
`xy_to_byte_offset()` (matching `click_pressed_cb` behavior at
54+
`hex-input-edit.c:1806`), clear any selection, then paste from the
55+
primary clipboard via the refactored `do_paste`.
56+
- [ ] 1e. Test: select in another app → middle-click in input → text
57+
inserts at click point. Select in our input → middle-click in another
58+
app (or another instance) → text inserts there.
59+
60+
## 2. Drag-and-drop receive (text / URLs / files)
61+
62+
No `GtkDropTarget` is attached to the input. `src/fe-gtk/maingui.c:5340` and
63+
`src/fe-gtk/userlistgui.c:1292` show the rest of the app accepting drops, but
64+
the input silently rejects dragged text, URLs, and files. Dragging a URL
65+
from a browser onto the input — which would natively insert the URL —
66+
currently does nothing.
67+
68+
- [ ] 2a. Attach a `GtkDropTarget` accepting `G_TYPE_STRING` (text/plain,
69+
text/uri-list) for inserting text/URL at the drop position.
70+
- [ ] 2b. Decide policy for `G_TYPE_FILE` drops onto the input. Options:
71+
insert the filename, insert a `file://` URI, trigger a DCC SEND
72+
offer (matches userlist behavior at `userlistgui.c:1292`), or
73+
reject. Probably "insert URI" is the least surprising default; DCC
74+
should require dropping on a user/channel, not on the input box.
75+
- [ ] 2c. Hit-test the drop coordinate with `xy_to_byte_offset()` so text
76+
is inserted at the drop point, not the current cursor.
77+
78+
## 3. Drag-and-drop source (drag selection out)
79+
80+
There's no `GtkDragSource` on the selection, so you can't drag selected text
81+
out of the input into other applications or rearrange it within the field.
82+
83+
- [ ] 3a. Attach a `GtkDragSource` whose content provider returns the
84+
currently selected text. Activate it only when a drag starts on a
85+
point inside the existing selection (so plain drags on unselected
86+
text still extend selection via the existing `drag_update_cb`).
87+
- [ ] 3b. On successful `GDK_ACTION_MOVE` drop, delete the source selection
88+
from the input (matching `GtkText` semantics).
89+
90+
## 4. Accessibility
91+
92+
`GtkText` declares `GTK_ACCESSIBLE_ROLE_TEXT_BOX` and pushes value, caret,
93+
and selection updates to ATK / AT-SPI. This widget makes no
94+
`gtk_accessible_*` calls and sets no role, so screen readers (Orca on Linux,
95+
NVDA via the AT-SPI bridge) see an opaque custom widget — they can't read
96+
what you're typing, announce the caret, or convey the selection.
97+
98+
- [ ] 4a. Set `GTK_ACCESSIBLE_ROLE_TEXT_BOX` (or `_SEARCH_BOX`?) via
99+
`gtk_widget_class_set_accessible_role()` in `class_init`.
100+
- [ ] 4b. Push value updates with
101+
`gtk_accessible_update_property(GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT,
102+
…)` whenever `priv->text` changes.
103+
- [ ] 4c. Push caret/selection state via
104+
`gtk_accessible_update_state(GTK_ACCESSIBLE_STATE_SELECTED, …)` and
105+
the appropriate caret-position property — check exactly which
106+
GTK4 properties `GtkText` uses and mirror them.
107+
- [ ] 4d. Manual test with Orca: type, select, copy/paste, navigate, and
108+
confirm the spoken output matches what `GtkText` would say.
109+
110+
## 5. Emoji chooser shortcut
111+
112+
`GtkText` binds `Ctrl+.` / `Ctrl+;` to an `insert-emoji` action that pops up
113+
`GtkEmojiChooser`. The actions installed at `hex-input-edit.c:2619-2627`
114+
don't include one. Given that emoji rendering is the marquee feature of
115+
this widget, the absence of an emoji *picker* is conspicuous.
116+
117+
- [ ] 5a. Install an `edit.insert-emoji` action via
118+
`gtk_widget_class_install_action()` that constructs a
119+
`GtkEmojiChooser`, anchors it at the caret position, and inserts the
120+
chosen emoji at the cursor.
121+
- [ ] 5b. Bind `Ctrl+.` and `Ctrl+;` to the action via
122+
`gtk_widget_class_add_binding_action()`.
123+
- [ ] 5c. Append an "Insert Emoji" item to the right-click context menu
124+
(currently built in `show_context_menu`, populated at
125+
`hex-input-edit.c:1422`).
126+
127+
## 6. Input-methods menu in context menu
128+
129+
`GtkEntry`'s right-click menu includes "Input Methods" and "Insert Unicode
130+
Control Character" submenus, driven by
131+
`gtk_im_multicontext_append_menuitems`. Not present here. Matters for
132+
users on non-Latin IME configurations.
133+
134+
- [ ] 6a. Append the IM submenu to the right-click `GMenu` in
135+
`show_context_menu`. (GTK4 changed the API from GTK3 — verify the
136+
modern equivalent; may need to build the submenu manually from
137+
`gtk_im_multicontext_get_context_ids()`.)
138+
- [ ] 6b. Append a Unicode-control-characters submenu (LRM/RLM/ZWJ/ZWNJ
139+
etc.) — or decide it's not worth the surface area and skip.
140+
141+
## 7. Theme integration (caret blink, selection color)
142+
143+
The widget runs its own caret blink timer and sources colors from the xtext
144+
palette. This means it ignores:
145+
146+
- `gtk-cursor-blink` (whether caret blinks at all)
147+
- `gtk-cursor-blink-time` (period)
148+
- `gtk-cursor-blink-timeout` (when to stop blinking after idle)
149+
- Theme selection background/foreground
150+
151+
This is a deliberate tradeoff — we want the input to match xtext's color
152+
scheme, not the GTK theme. But the blink settings probably should be
153+
honored even if the colors aren't.
154+
155+
- [ ] 7a. Read `gtk-cursor-blink`, `-time`, `-timeout` from
156+
`GtkSettings` and feed them into the existing blink timer. Disable
157+
blinking entirely when `gtk-cursor-blink` is FALSE.
158+
- [ ] 7b. Decide whether selection color should follow theme or palette.
159+
Currently it follows the palette; leaving this as-is is defensible
160+
since xtext does the same.
161+
162+
## 8. BiDi / RTL correctness
163+
164+
`GtkText` handles logical-vs-visual cursor movement around RTL runs,
165+
mirrored arrow-key meaning (right-arrow moves to end-of-run in RTL), and
166+
Unicode override character insertion. Custom byte-offset-driven cursor
167+
code typically doesn't get this right by default.
168+
169+
- [ ] 8a. Test with Hebrew and Arabic text: arrow keys, Home/End,
170+
Shift+selection, double-click word selection, mouse positioning.
171+
Document what breaks.
172+
- [ ] 8b. If broken: switch cursor movement to use Pango's
173+
`pango_layout_move_cursor_visually()` instead of raw byte stepping.
174+
175+
## 9. Touch / long-press selection
176+
177+
`GtkText` shows touch selection bubbles and a long-press popover with
178+
cut/copy/paste/select-all. No `GtkGestureLongPress` is registered here.
179+
Not relevant on a typical desktop but matters for touchscreens and Wayland
180+
tablets.
181+
182+
- [ ] 9a. Decide whether PoxChat supports touch as a target. If yes, add a
183+
`GtkGestureLongPress` that shows a popover with the same actions as
184+
the right-click context menu. If no, document the decision and
185+
close the item.
186+
187+
## 10. Undo coalescing audit
188+
189+
`GtkText` coalesces consecutive keystrokes into one undo step and breaks at
190+
word boundaries / IM commit. Verify what `priv->undo_stack`
191+
(`hex-input-edit.c:1072+`) does today.
192+
193+
- [ ] 10a. Type a sentence, press Ctrl+Z. Does it undo the whole sentence,
194+
one word, or one character? Native entries undo by word.
195+
- [ ] 10b. If per-character, add coalescing: extend the current undo
196+
snapshot when the new input is contiguous and not whitespace.
197+
198+
## 11. Selection auto-scroll during drag past widget edges
199+
200+
If you drag-select past the right edge of the widget on a multi-line or
201+
overflowing input, native entries auto-scroll. Verify the custom one does.
202+
203+
- [ ] 11a. Type a long line, drag from the middle to past the right edge,
204+
hold. Does the selection extend and the view scroll?
205+
- [ ] 11b. If not: in `drag_update_cb` (`hex-input-edit.c:1880`), detect
206+
drag past widget bounds and start a scroll-while-held tick callback.
207+
208+
## 12. Overwrite mode (Insert key)
209+
210+
Minor. `GtkText` toggles overwrite mode with the Insert key. Custom widget
211+
doesn't.
212+
213+
- [ ] 12a. Add an `overwrite` boolean to `HexInputEditPriv`, toggle it on
214+
Insert key, and change `insert_at_cursor`/caret rendering when true.
215+
Caret should render as a block in overwrite mode.
216+
217+
---
218+
219+
## Out of scope (intentional non-goals)
220+
221+
- **Password mode / invisible char.** Not relevant for chat input.
222+
- **`GtkEntryCompletion` API surface.** PoxChat has tab-completion in
223+
`fkeys.c`; the GTK completion popdown protocol is a separate concept
224+
and probably not worth re-creating.
225+
- **Stylus pressure events.** Not meaningful for a text entry.

IRCV3_IMPLEMENTATION_PLAN.md renamed to docs/projects/IRCV3_IMPLEMENTATION_PLAN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,7 +1455,7 @@ void poxchat_markread(poxchat_plugin *ph, const char *target, const char *msgid)
14551455
14561456
## Implementation Task List (Phased)
14571457
1458-
This section breaks down implementation into concrete tasks organized by dependency order. Tasks reference UX decisions from [IRCV3_UX_AUDIT.md](docs/IRCV3_UX_AUDIT.md).
1458+
This section breaks down implementation into concrete tasks organized by dependency order. Tasks reference UX decisions from [IRCV3_UX_AUDIT.md](IRCV3_UX_AUDIT.md).
14591459
14601460
### Legend
14611461
- ✅ Complete
@@ -1838,7 +1838,7 @@ While the echo-message capability is negotiated and basic self-echo detection ex
18381838
18391839
### xtext Modernization Status
18401840
1841-
See [XTEXT_MODERNIZATION_PLAN.md](docs/XTEXT_MODERNIZATION_PLAN.md) for detailed xtext buffer modernization status.
1841+
See [XTEXT_MODERNIZATION_PLAN.md](XTEXT_MODERNIZATION_PLAN.md) for detailed xtext buffer modernization status.
18421842
18431843
| Phase | Status |
18441844
|-------|--------|
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)