Open the picker under the message it is for
It was a panel at the bottom of the window, which left the message it was about somewhere else on the screen — sometimes several lines up, sometimes scrolled off. Choosing a reaction is something done *to* a message, so the picker now opens in the list itself, directly under the line whose ☺ was clicked, and the conversation carries on below it. The grid is no longer a scroll of its own. A scroll inside the message list takes the wheel away from the list it is standing in, and — nested in one — egui gave it whatever height was going rather than the height it asked for, so the bottom row came out sliced in half. It lays out whole instead, bounded by how much it will show: twelve rows, with "and N more" and the search box for what is past that. The chat screen goes back to what it was — no reserve for a panel that is no longer there, and the way back to the present keeps its row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
07da0eb parent: ad3a77d modified
src/frq/app.jolt +74 -96 | @@ -285,6 +285,71 @@ | ||
| 285 | 285 | :mine (s/my-reaction? m emoji) |
| 286 | 286 | :on-click #(s/toggle-reaction! channel m emoji)}])])) |
| 287 | 287 | |
| 288 | +(def ^:private picker-columns | |
| 289 | + "Emoji to a row, at the picker's own size. Narrow enough that the grid fits a | |
| 290 | + phone-width window, which is the width this app is laid out for." | |
| 291 | + 9) | |
| 292 | + | |
| 293 | +(def ^:private picker-size 20) | |
| 294 | + | |
| 295 | +;; A row of chips, and the most the grid may take before it scrolls. | |
| 296 | + | |
| 297 | + | |
| 298 | +(defn emoji-picker | |
| 299 | + "The whole set to choose from, under the message it is for. | |
| 300 | + | |
| 301 | + It opens in the list itself, directly under the line it was opened on: | |
| 302 | + choosing a reaction is something done *to* a message, and the message has to | |
| 303 | + stay in front of the reader while it is chosen — which is not something a | |
| 304 | + panel at the bottom of the window, or a screen of its own, can promise. | |
| 305 | + | |
| 306 | + It opens on the emoji people actually react with; the groups and the search | |
| 307 | + box are for the rest." | |
| 308 | + [] | |
| 309 | + (let [shown (s/picker-emoji) | |
| 310 | + over (max 0 (- (count shown) s/picker-limit)) | |
| 311 | + rows (partition-all picker-columns (take s/picker-limit shown)) | |
| 312 | + searching? (seq (str/trim @s/emoji-search))] | |
| 313 | + [:vbox {:key :picker :spacing 4} | |
| 314 | + [:hbox {:spacing 6} | |
| 315 | + [:entry {:text @s/emoji-search | |
| 316 | + :width-request 240 | |
| 317 | + :placeholder "Search emoji" | |
| 318 | + :on-change #(reset! s/emoji-search %)}] | |
| 319 | + [:button {:label "✕" :on-click s/close-picker!}]] | |
| 320 | + ;; The groups are what the search box is not: a way in for someone who has | |
| 321 | + ;; no word for what they want. Their first word is enough to tell them | |
| 322 | + ;; apart, and is what keeps them to two rows. They give way to the | |
| 323 | + ;; search's own answer while something is typed. | |
| 324 | + [:vbox {:key :groups :spacing 4} | |
| 325 | + (when-not searching? | |
| 326 | + (for [[i row] (map-indexed vector (partition-all 5 (cons nil s/emoji-groups)))] | |
| 327 | + [:hbox {:key i :spacing 4} | |
| 328 | + (for [g row] | |
| 329 | + [:button {:key (or g "popular") | |
| 330 | + :label (if g (first (str/split g #" ")) "Popular") | |
| 331 | + :kind (if (= g @s/emoji-group) :primary :normal) | |
| 332 | + :on-click #(reset! s/emoji-group g)}])]))] | |
| 333 | + ;; The grid itself, not a scroll around it: the picker sits inside the | |
| 334 | + ;; message list, and a scroll within a scroll takes the wheel away from | |
| 335 | + ;; the conversation it is standing in. What it shows is bounded instead, | |
| 336 | + ;; and the search box is how you reach past that. | |
| 337 | + [:vbox {:key :grid :spacing 2} | |
| 338 | + (if (seq rows) | |
| 339 | + (for [[i row] (map-indexed vector rows)] | |
| 340 | + [:hbox {:key i :spacing 2} | |
| 341 | + (for [[glyph] row] | |
| 342 | + [:reaction {:key glyph | |
| 343 | + :emoji glyph | |
| 344 | + :size picker-size | |
| 345 | + :count 0 | |
| 346 | + :on-click #(s/react-from-picker! glyph)}])]) | |
| 347 | + [:dim-label {:label "No emoji by that name."}])] | |
| 348 | + ;; What was left out, said rather than silently dropped. | |
| 349 | + [:vbox {:key :more} | |
| 350 | + (when (pos? over) | |
| 351 | + [:dim-label {:label (str "and " over " more — keep typing to narrow it")}])]])) | |
| 352 | + | |
| 288 | 353 | (defn message-row |
| 289 | 354 | "One message. `prev` is the message above it, which decides whether this one |
| 290 | 355 | repeats the sender. |
| @@ -365,6 +430,11 @@ | ||
| 365 | 430 | [:vbox {:key :reactions-row :margin-top 6 :margin-bottom 2} |
| 366 | 431 | (when (and (:id m) (not (:system? m)) (seq (:reactions m))) |
| 367 | 432 | [reaction-row @s/current m])] |
| 433 | + ;; And the picker, when this is the message it was opened on: under the | |
| 434 | + ;; line it is about, where the reader is already looking. | |
| 435 | + [:vbox {:key :picker :margin-bottom 4} | |
| 436 | + (when (= (:id m) (:id @s/reacting)) | |
| 437 | + [emoji-picker])] | |
| 368 | 438 | ;; Pictures under the line that linked them. The link stays: it is what a |
| 369 | 439 | ;; failed fetch, an unsupported format, or a phone with no TLS leaves you. |
| 370 | 440 | [:vbox {:key :images :spacing 4} |
| @@ -419,86 +489,6 @@ | ||
| 419 | 489 | ;; Clicking the picture closes it too — the same gesture that opened it. |
| 420 | 490 | [:image {:src path :max-height 20000 :on-click #(reset! s/lightbox nil)}]]])) |
| 421 | 491 | |
| 422 | -(def ^:private picker-columns | |
| 423 | - "Emoji to a row, at the picker's own size. Narrow enough that the grid fits a | |
| 424 | - phone-width window, which is the width this app is laid out for." | |
| 425 | - 9) | |
| 426 | - | |
| 427 | -(def ^:private picker-size 20) | |
| 428 | - | |
| 429 | -(def ^:private picker-row-height 48) | |
| 430 | -(def ^:private picker-grid-max 170) | |
| 431 | - | |
| 432 | -(defn- picker-reserve | |
| 433 | - "How much room the picker panel wants under the message list. | |
| 434 | - | |
| 435 | - Measured rather than fixed: a panel is as tall as the grid it is showing, and | |
| 436 | - a reserve that assumed the tallest would leave a hole above a short one — the | |
| 437 | - popular row is two rows deep, a group is as deep as the grid allows." | |
| 438 | - [] | |
| 439 | - (let [n (min s/picker-limit (count (s/picker-emoji))) | |
| 440 | - rows (quot (+ n picker-columns -1) picker-columns) | |
| 441 | - searching? (seq (str/trim @s/emoji-search))] | |
| 442 | - (+ 82 ; the separator and compose bar | |
| 443 | - 44 ; the search box | |
| 444 | - (if searching? 0 88) ; the two rows of groups | |
| 445 | - (min picker-grid-max (* picker-row-height rows)) | |
| 446 | - ;; The spacing between the panel's own rows, which is small and adds up | |
| 447 | - ;; — a reserve that came up short would put the compose bar under the | |
| 448 | - ;; bottom of the window. | |
| 449 | - 60))) | |
| 450 | - | |
| 451 | -(defn emoji-picker | |
| 452 | - "The whole set to choose from, over the compose bar, for one message. | |
| 453 | - | |
| 454 | - A panel under the conversation rather than a screen of its own: choosing a | |
| 455 | - reaction is something done *to* a message, and a picker that hid the message | |
| 456 | - would be asking about something no longer on screen. It takes the bottom of | |
| 457 | - the window; the list above it gives up that much room and keeps the rest. | |
| 458 | - | |
| 459 | - It opens on the emoji people actually react with; the groups and the search | |
| 460 | - box are for the rest." | |
| 461 | - [] | |
| 462 | - (let [shown (s/picker-emoji) | |
| 463 | - over (max 0 (- (count shown) s/picker-limit)) | |
| 464 | - rows (partition-all picker-columns (take s/picker-limit shown)) | |
| 465 | - searching? (seq (str/trim @s/emoji-search))] | |
| 466 | - [:vbox {:key :picker :spacing 4} | |
| 467 | - [:hbox {:spacing 6} | |
| 468 | - [:entry {:text @s/emoji-search | |
| 469 | - :width-request 240 | |
| 470 | - :placeholder "Search emoji" | |
| 471 | - :on-change #(reset! s/emoji-search %)}] | |
| 472 | - [:button {:label "✕" :on-click s/close-picker!}]] | |
| 473 | - ;; The groups are what the search box is not: a way in for someone who has | |
| 474 | - ;; no word for what they want. Their first word is enough to tell them | |
| 475 | - ;; apart, and is what keeps them to two rows. They give way to the | |
| 476 | - ;; search's own answer while something is typed. | |
| 477 | - [:vbox {:key :groups :spacing 4} | |
| 478 | - (when-not searching? | |
| 479 | - (for [[i row] (map-indexed vector (partition-all 5 (cons nil s/emoji-groups)))] | |
| 480 | - [:hbox {:key i :spacing 4} | |
| 481 | - (for [g row] | |
| 482 | - [:button {:key (or g "popular") | |
| 483 | - :label (if g (first (str/split g #" ")) "Popular") | |
| 484 | - :kind (if (= g @s/emoji-group) :primary :normal) | |
| 485 | - :on-click #(reset! s/emoji-group g)}])]))] | |
| 486 | - [:scroll {:orientation :vertical :max-height picker-grid-max} | |
| 487 | - (if (seq rows) | |
| 488 | - (for [[i row] (map-indexed vector rows)] | |
| 489 | - [:hbox {:key i :spacing 2} | |
| 490 | - (for [[glyph] row] | |
| 491 | - [:reaction {:key glyph | |
| 492 | - :emoji glyph | |
| 493 | - :size picker-size | |
| 494 | - :count 0 | |
| 495 | - :on-click #(s/react-from-picker! glyph)}])]) | |
| 496 | - [:dim-label {:label "No emoji by that name."}])] | |
| 497 | - ;; What was left out, said rather than silently dropped. | |
| 498 | - [:vbox {:key :more} | |
| 499 | - (when (pos? over) | |
| 500 | - [:dim-label {:label (str "and " over " more — keep typing to narrow it")}])]])) | |
| 501 | - | |
| 502 | 492 | (defn chat-screen [] |
| 503 | 493 | (let [name @s/current |
| 504 | 494 | buffer (get @s/channels name)] |
| @@ -522,12 +512,7 @@ | ||
| 522 | 512 | ;; move the compose bar, and should: it appears because the |
| 523 | 513 | ;; reader asked to answer something, unlike the jump button, |
| 524 | 514 | ;; which appears on its own and must not shift what is under it. |
| 525 | - ;; And the picker's, which takes the bottom of the window | |
| 526 | - ;; while it is open — the list gives up that room rather than | |
| 527 | - ;; being painted under it. | |
| 528 | - :reserve (cond @s/reacting (picker-reserve) | |
| 529 | - @s/replying-to 150 | |
| 530 | - :else 116) | |
| 515 | + :reserve (if @s/replying-to 150 116) | |
| 531 | 516 | :stick-to-bottom true |
| 532 | 517 | :scroll-to-bottom @s/jump-tick |
| 533 | 518 | :on-change #(reset! s/at-present? (= "end" %))} |
| @@ -538,17 +523,10 @@ | ||
| 538 | 523 | ;; back to the present belongs next to the thing that puts you there. |
| 539 | 524 | ;; The row keeps its height whether or not the button is in it, so the |
| 540 | 525 | ;; compose bar below stays where the reader last saw it. |
| 541 | - ;; Choosing an emoji is the only thing being done while the picker is | |
| 542 | - ;; open, so it takes the row the way back to the present would have had. | |
| 543 | - [:vbox {:key :picker-panel} | |
| 544 | - (when @s/reacting [emoji-picker])] | |
| 545 | 526 | [:vbox {:key :jump} |
| 546 | - (cond | |
| 547 | - ;; Not even the spacer while the picker is open: the panel is what is | |
| 548 | - ;; holding the compose bar's place, and a gap under it is just a gap. | |
| 549 | - @s/reacting nil | |
| 550 | - @s/at-present? [:spacer {:size 34}] | |
| 551 | - :else [:button {:label "↓ Jump to present" :on-click s/jump-to-present!}])] | |
| 527 | + (if @s/at-present? | |
| 528 | + [:spacer {:size 34}] | |
| 529 | + [:button {:label "↓ Jump to present" :on-click s/jump-to-present!}])] | |
| 552 | 530 | [:separator {}] |
| 553 | 531 | ;; What the draft is answering, directly above where it is being typed. |
| 554 | 532 | [:vbox {:key :replying} |
| @@ -285,6 +285,71 @@ | |||
| 285 | :mine (s/my-reaction? m emoji) | 285 | :mine (s/my-reaction? m emoji) |
| 286 | :on-click #(s/toggle-reaction! channel m emoji)}])])) | 286 | :on-click #(s/toggle-reaction! channel m emoji)}])])) |
| 287 | 287 | ||
| 288 | +(def ^:private picker-columns | ||
| 289 | + "Emoji to a row, at the picker's own size. Narrow enough that the grid fits a | ||
| 290 | + phone-width window, which is the width this app is laid out for." | ||
| 291 | + 9) | ||
| 292 | + | ||
| 293 | +(def ^:private picker-size 20) | ||
| 294 | + | ||
| 295 | +;; A row of chips, and the most the grid may take before it scrolls. | ||
| 296 | + | ||
| 297 | + | ||
| 298 | +(defn emoji-picker | ||
| 299 | + "The whole set to choose from, under the message it is for. | ||
| 300 | + | ||
| 301 | + It opens in the list itself, directly under the line it was opened on: | ||
| 302 | + choosing a reaction is something done *to* a message, and the message has to | ||
| 303 | + stay in front of the reader while it is chosen — which is not something a | ||
| 304 | + panel at the bottom of the window, or a screen of its own, can promise. | ||
| 305 | + | ||
| 306 | + It opens on the emoji people actually react with; the groups and the search | ||
| 307 | + box are for the rest." | ||
| 308 | + [] | ||
| 309 | + (let [shown (s/picker-emoji) | ||
| 310 | + over (max 0 (- (count shown) s/picker-limit)) | ||
| 311 | + rows (partition-all picker-columns (take s/picker-limit shown)) | ||
| 312 | + searching? (seq (str/trim @s/emoji-search))] | ||
| 313 | + [:vbox {:key :picker :spacing 4} | ||
| 314 | + [:hbox {:spacing 6} | ||
| 315 | + [:entry {:text @s/emoji-search | ||
| 316 | + :width-request 240 | ||
| 317 | + :placeholder "Search emoji" | ||
| 318 | + :on-change #(reset! s/emoji-search %)}] | ||
| 319 | + [:button {:label "✕" :on-click s/close-picker!}]] | ||
| 320 | + ;; The groups are what the search box is not: a way in for someone who has | ||
| 321 | + ;; no word for what they want. Their first word is enough to tell them | ||
| 322 | + ;; apart, and is what keeps them to two rows. They give way to the | ||
| 323 | + ;; search's own answer while something is typed. | ||
| 324 | + [:vbox {:key :groups :spacing 4} | ||
| 325 | + (when-not searching? | ||
| 326 | + (for [[i row] (map-indexed vector (partition-all 5 (cons nil s/emoji-groups)))] | ||
| 327 | + [:hbox {:key i :spacing 4} | ||
| 328 | + (for [g row] | ||
| 329 | + [:button {:key (or g "popular") | ||
| 330 | + :label (if g (first (str/split g #" ")) "Popular") | ||
| 331 | + :kind (if (= g @s/emoji-group) :primary :normal) | ||
| 332 | + :on-click #(reset! s/emoji-group g)}])]))] | ||
| 333 | + ;; The grid itself, not a scroll around it: the picker sits inside the | ||
| 334 | + ;; message list, and a scroll within a scroll takes the wheel away from | ||
| 335 | + ;; the conversation it is standing in. What it shows is bounded instead, | ||
| 336 | + ;; and the search box is how you reach past that. | ||
| 337 | + [:vbox {:key :grid :spacing 2} | ||
| 338 | + (if (seq rows) | ||
| 339 | + (for [[i row] (map-indexed vector rows)] | ||
| 340 | + [:hbox {:key i :spacing 2} | ||
| 341 | + (for [[glyph] row] | ||
| 342 | + [:reaction {:key glyph | ||
| 343 | + :emoji glyph | ||
| 344 | + :size picker-size | ||
| 345 | + :count 0 | ||
| 346 | + :on-click #(s/react-from-picker! glyph)}])]) | ||
| 347 | + [:dim-label {:label "No emoji by that name."}])] | ||
| 348 | + ;; What was left out, said rather than silently dropped. | ||
| 349 | + [:vbox {:key :more} | ||
| 350 | + (when (pos? over) | ||
| 351 | + [:dim-label {:label (str "and " over " more — keep typing to narrow it")}])]])) | ||
| 352 | + | ||
| 288 | (defn message-row | 353 | (defn message-row |
| 289 | "One message. `prev` is the message above it, which decides whether this one | 354 | "One message. `prev` is the message above it, which decides whether this one |
| 290 | repeats the sender. | 355 | repeats the sender. |
| @@ -365,6 +430,11 @@ | |||
| 365 | [:vbox {:key :reactions-row :margin-top 6 :margin-bottom 2} | 430 | [:vbox {:key :reactions-row :margin-top 6 :margin-bottom 2} |
| 366 | (when (and (:id m) (not (:system? m)) (seq (:reactions m))) | 431 | (when (and (:id m) (not (:system? m)) (seq (:reactions m))) |
| 367 | [reaction-row @s/current m])] | 432 | [reaction-row @s/current m])] |
| 433 | + ;; And the picker, when this is the message it was opened on: under the | ||
| 434 | + ;; line it is about, where the reader is already looking. | ||
| 435 | + [:vbox {:key :picker :margin-bottom 4} | ||
| 436 | + (when (= (:id m) (:id @s/reacting)) | ||
| 437 | + [emoji-picker])] | ||
| 368 | ;; Pictures under the line that linked them. The link stays: it is what a | 438 | ;; Pictures under the line that linked them. The link stays: it is what a |
| 369 | ;; failed fetch, an unsupported format, or a phone with no TLS leaves you. | 439 | ;; failed fetch, an unsupported format, or a phone with no TLS leaves you. |
| 370 | [:vbox {:key :images :spacing 4} | 440 | [:vbox {:key :images :spacing 4} |
| @@ -419,86 +489,6 @@ | |||
| 419 | ;; Clicking the picture closes it too — the same gesture that opened it. | 489 | ;; Clicking the picture closes it too — the same gesture that opened it. |
| 420 | [:image {:src path :max-height 20000 :on-click #(reset! s/lightbox nil)}]]])) | 490 | [:image {:src path :max-height 20000 :on-click #(reset! s/lightbox nil)}]]])) |
| 421 | 491 | ||
| 422 | -(def ^:private picker-columns | ||
| 423 | - "Emoji to a row, at the picker's own size. Narrow enough that the grid fits a | ||
| 424 | - phone-width window, which is the width this app is laid out for." | ||
| 425 | - 9) | ||
| 426 | - | ||
| 427 | -(def ^:private picker-size 20) | ||
| 428 | - | ||
| 429 | -(def ^:private picker-row-height 48) | ||
| 430 | -(def ^:private picker-grid-max 170) | ||
| 431 | - | ||
| 432 | -(defn- picker-reserve | ||
| 433 | - "How much room the picker panel wants under the message list. | ||
| 434 | - | ||
| 435 | - Measured rather than fixed: a panel is as tall as the grid it is showing, and | ||
| 436 | - a reserve that assumed the tallest would leave a hole above a short one — the | ||
| 437 | - popular row is two rows deep, a group is as deep as the grid allows." | ||
| 438 | - [] | ||
| 439 | - (let [n (min s/picker-limit (count (s/picker-emoji))) | ||
| 440 | - rows (quot (+ n picker-columns -1) picker-columns) | ||
| 441 | - searching? (seq (str/trim @s/emoji-search))] | ||
| 442 | - (+ 82 ; the separator and compose bar | ||
| 443 | - 44 ; the search box | ||
| 444 | - (if searching? 0 88) ; the two rows of groups | ||
| 445 | - (min picker-grid-max (* picker-row-height rows)) | ||
| 446 | - ;; The spacing between the panel's own rows, which is small and adds up | ||
| 447 | - ;; — a reserve that came up short would put the compose bar under the | ||
| 448 | - ;; bottom of the window. | ||
| 449 | - 60))) | ||
| 450 | - | ||
| 451 | -(defn emoji-picker | ||
| 452 | - "The whole set to choose from, over the compose bar, for one message. | ||
| 453 | - | ||
| 454 | - A panel under the conversation rather than a screen of its own: choosing a | ||
| 455 | - reaction is something done *to* a message, and a picker that hid the message | ||
| 456 | - would be asking about something no longer on screen. It takes the bottom of | ||
| 457 | - the window; the list above it gives up that much room and keeps the rest. | ||
| 458 | - | ||
| 459 | - It opens on the emoji people actually react with; the groups and the search | ||
| 460 | - box are for the rest." | ||
| 461 | - [] | ||
| 462 | - (let [shown (s/picker-emoji) | ||
| 463 | - over (max 0 (- (count shown) s/picker-limit)) | ||
| 464 | - rows (partition-all picker-columns (take s/picker-limit shown)) | ||
| 465 | - searching? (seq (str/trim @s/emoji-search))] | ||
| 466 | - [:vbox {:key :picker :spacing 4} | ||
| 467 | - [:hbox {:spacing 6} | ||
| 468 | - [:entry {:text @s/emoji-search | ||
| 469 | - :width-request 240 | ||
| 470 | - :placeholder "Search emoji" | ||
| 471 | - :on-change #(reset! s/emoji-search %)}] | ||
| 472 | - [:button {:label "✕" :on-click s/close-picker!}]] | ||
| 473 | - ;; The groups are what the search box is not: a way in for someone who has | ||
| 474 | - ;; no word for what they want. Their first word is enough to tell them | ||
| 475 | - ;; apart, and is what keeps them to two rows. They give way to the | ||
| 476 | - ;; search's own answer while something is typed. | ||
| 477 | - [:vbox {:key :groups :spacing 4} | ||
| 478 | - (when-not searching? | ||
| 479 | - (for [[i row] (map-indexed vector (partition-all 5 (cons nil s/emoji-groups)))] | ||
| 480 | - [:hbox {:key i :spacing 4} | ||
| 481 | - (for [g row] | ||
| 482 | - [:button {:key (or g "popular") | ||
| 483 | - :label (if g (first (str/split g #" ")) "Popular") | ||
| 484 | - :kind (if (= g @s/emoji-group) :primary :normal) | ||
| 485 | - :on-click #(reset! s/emoji-group g)}])]))] | ||
| 486 | - [:scroll {:orientation :vertical :max-height picker-grid-max} | ||
| 487 | - (if (seq rows) | ||
| 488 | - (for [[i row] (map-indexed vector rows)] | ||
| 489 | - [:hbox {:key i :spacing 2} | ||
| 490 | - (for [[glyph] row] | ||
| 491 | - [:reaction {:key glyph | ||
| 492 | - :emoji glyph | ||
| 493 | - :size picker-size | ||
| 494 | - :count 0 | ||
| 495 | - :on-click #(s/react-from-picker! glyph)}])]) | ||
| 496 | - [:dim-label {:label "No emoji by that name."}])] | ||
| 497 | - ;; What was left out, said rather than silently dropped. | ||
| 498 | - [:vbox {:key :more} | ||
| 499 | - (when (pos? over) | ||
| 500 | - [:dim-label {:label (str "and " over " more — keep typing to narrow it")}])]])) | ||
| 501 | - | ||
| 502 | (defn chat-screen [] | 492 | (defn chat-screen [] |
| 503 | (let [name @s/current | 493 | (let [name @s/current |
| 504 | buffer (get @s/channels name)] | 494 | buffer (get @s/channels name)] |
| @@ -522,12 +512,7 @@ | |||
| 522 | ;; move the compose bar, and should: it appears because the | 512 | ;; move the compose bar, and should: it appears because the |
| 523 | ;; reader asked to answer something, unlike the jump button, | 513 | ;; reader asked to answer something, unlike the jump button, |
| 524 | ;; which appears on its own and must not shift what is under it. | 514 | ;; which appears on its own and must not shift what is under it. |
| 525 | - ;; And the picker's, which takes the bottom of the window | 515 | + :reserve (if @s/replying-to 150 116) |
| 526 | - ;; while it is open — the list gives up that room rather than | ||
| 527 | - ;; being painted under it. | ||
| 528 | - :reserve (cond @s/reacting (picker-reserve) | ||
| 529 | - @s/replying-to 150 | ||
| 530 | - :else 116) | ||
| 531 | :stick-to-bottom true | 516 | :stick-to-bottom true |
| 532 | :scroll-to-bottom @s/jump-tick | 517 | :scroll-to-bottom @s/jump-tick |
| 533 | :on-change #(reset! s/at-present? (= "end" %))} | 518 | :on-change #(reset! s/at-present? (= "end" %))} |
| @@ -538,17 +523,10 @@ | |||
| 538 | ;; back to the present belongs next to the thing that puts you there. | 523 | ;; back to the present belongs next to the thing that puts you there. |
| 539 | ;; The row keeps its height whether or not the button is in it, so the | 524 | ;; The row keeps its height whether or not the button is in it, so the |
| 540 | ;; compose bar below stays where the reader last saw it. | 525 | ;; compose bar below stays where the reader last saw it. |
| 541 | - ;; Choosing an emoji is the only thing being done while the picker is | ||
| 542 | - ;; open, so it takes the row the way back to the present would have had. | ||
| 543 | - [:vbox {:key :picker-panel} | ||
| 544 | - (when @s/reacting [emoji-picker])] | ||
| 545 | [:vbox {:key :jump} | 526 | [:vbox {:key :jump} |
| 546 | - (cond | 527 | + (if @s/at-present? |
| 547 | - ;; Not even the spacer while the picker is open: the panel is what is | 528 | + [:spacer {:size 34}] |
| 548 | - ;; holding the compose bar's place, and a gap under it is just a gap. | 529 | + [:button {:label "↓ Jump to present" :on-click s/jump-to-present!}])] |
| 549 | - @s/reacting nil | ||
| 550 | - @s/at-present? [:spacer {:size 34}] | ||
| 551 | - :else [:button {:label "↓ Jump to present" :on-click s/jump-to-present!}])] | ||
| 552 | [:separator {}] | 530 | [:separator {}] |
| 553 | ;; What the draft is answering, directly above where it is being typed. | 531 | ;; What the draft is answering, directly above where it is being typed. |
| 554 | [:vbox {:key :replying} | 532 | [:vbox {:key :replying} |
modified
src/frq/state.jolt +5 -3 | @@ -542,9 +542,11 @@ | ||
| 542 | 542 | emoji/groups) |
| 543 | 543 | |
| 544 | 544 | (def picker-limit |
| 545 | - "How many glyphs one screen of the picker will show. A search that matches | |
| 546 | - more says so rather than laying out hundreds of pictures nobody scrolled to." | |
| 547 | - 240) | |
| 545 | + "How many glyphs the picker will lay out at once — twelve rows of the nine it | |
| 546 | + fits across. It stands inside the message list, so what it shows pushes the | |
| 547 | + conversation down; a group that has more says so, and the search box is how | |
| 548 | + you reach the rest." | |
| 549 | + 108) | |
| 548 | 550 | |
| 549 | 551 | (defn picker-emoji |
| 550 | 552 | "What the picker is showing right now: the popular row, one group, or |
| @@ -542,9 +542,11 @@ | |||
| 542 | emoji/groups) | 542 | emoji/groups) |
| 543 | 543 | ||
| 544 | (def picker-limit | 544 | (def picker-limit |
| 545 | - "How many glyphs one screen of the picker will show. A search that matches | 545 | + "How many glyphs the picker will lay out at once — twelve rows of the nine it |
| 546 | - more says so rather than laying out hundreds of pictures nobody scrolled to." | 546 | + fits across. It stands inside the message list, so what it shows pushes the |
| 547 | - 240) | 547 | + conversation down; a group that has more says so, and the search box is how |
| 548 | + you reach the rest." | ||
| 549 | + 108) | ||
| 548 | 550 | ||
| 549 | (defn picker-emoji | 551 | (defn picker-emoji |
| 550 | "What the picker is showing right now: the popular row, one group, or | 552 | "What the picker is showing right now: the popular row, one group, or |