From 4cdb70033c8500935a57e4b2a7373ce56e9ae05a Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Tue, 8 Feb 2022 13:38:04 -0600 Subject: [PATCH] Prepare for placing 'open' buttons on article threads. --- spec/more_speech/content/article_spec.clj | 4 +- src/more_speech/content/article.clj | 35 ++++++------ src/more_speech/nostr/events.clj | 19 ++++++- src/more_speech/ui/application.clj | 14 ++++- src/more_speech/ui/article_window.clj | 66 +++++++++++++---------- src/more_speech/ui/cursor.clj | 57 +++++++++++--------- 6 files changed, 120 insertions(+), 75 deletions(-) diff --git a/spec/more_speech/content/article_spec.clj b/spec/more_speech/content/article_spec.clj index c583652..eab68c1 100644 --- a/spec/more_speech/content/article_spec.clj +++ b/spec/more_speech/content/article_spec.clj @@ -32,9 +32,9 @@ (should (s/valid? ::a/article article))) (it "is properly formatted" - (should= [ + (should= [:open-button :bold - "* Bob" + "Bob" :regular " (15)" :bold diff --git a/src/more_speech/content/article.clj b/src/more_speech/content/article.clj index ba78d0e..633970e 100644 --- a/src/more_speech/content/article.clj +++ b/src/more_speech/content/article.clj @@ -46,21 +46,26 @@ (abbreviate pubkey 8)) (defn markup-article [article] - [ - :bold - (str "* " (abbreviate-author (:author article))) - :regular - (str " (" (:thread-count article) ")") - :bold - :pos 40 - (:subject article) - :regular - :pos 80 - (format-time (:time article)) - :new-line - :multi-line (abbreviate-body (:body article)) - :line - :new-line]) + (let [ + thread-count (:thread-count article)] + [ + (if (> thread-count 0) + :open-button + :null-button) + :bold + (abbreviate-author (:author article)) + :regular + (str " (" thread-count ")") + :bold + :pos 40 + (:subject article) + :regular + :pos 80 + (format-time (:time article)) + :new-line + :multi-line (abbreviate-body (:body article)) + :line + :new-line])) (defn markup-author [[pubkey name]] [:bold diff --git a/src/more_speech/nostr/events.clj b/src/more_speech/nostr/events.clj index c93170a..2635b9f 100644 --- a/src/more_speech/nostr/events.clj +++ b/src/more_speech/nostr/events.clj @@ -1,8 +1,23 @@ (ns more-speech.nostr.events - (:require [more-speech.content.article :as article] + (:require [clojure.spec.alpha :as s] + [more-speech.content.article :as article] [clojure.data.json :as json] [more-speech.nostr.util :refer [hex-string->num]])) - +(s/def ::id number?) +(s/def ::pubkey number?) +(s/def ::created-at number?) +(s/def ::content string?) +(s/def ::sig number?) +(s/def ::tag (s/tuple keyword? number?)) +(s/def ::tags (s/coll-of ::tag)) +(s/def ::references (s/coll-of number?)) +(s/def ::event (s/keys :req-un [::id + ::pubkey + ::created-at + ::content + ::sig + ::tags + ::references])) (declare process-text-event) (defn process-event [{:keys [application] :as state} event] diff --git a/src/more_speech/ui/application.clj b/src/more_speech/ui/application.clj index 36ecdc2..d288783 100644 --- a/src/more_speech/ui/application.clj +++ b/src/more_speech/ui/application.clj @@ -1,5 +1,6 @@ (ns more-speech.ui.application - (:require [more-speech.ui.widget :refer [widget + (:require [clojure.spec.alpha :as s] + [more-speech.ui.widget :refer [widget draw-widget draw-child-widgets setup-child-widgets]] @@ -7,7 +8,16 @@ draw-article-window]] [more-speech.ui.author-window :refer [map->author-window draw-author-window]] - [more-speech.ui.graphics :as g])) + [more-speech.ui.graphics :as g] + [more-speech.nostr.events :as events])) + +(s/def ::nicknames (s/map-of number? string?)) +(s/def ::chronological-text-events (s/coll-of number?)) +(s/def ::text-event-map (s/map-of number? ::events/event)) +(s/def ::application (s/keys :req-un [::nicknames + ::chronological-text-events + ::text-event-map + ])) (declare setup-application) diff --git a/src/more_speech/ui/article_window.clj b/src/more_speech/ui/article_window.clj index 3f13eb0..1b4da3e 100644 --- a/src/more_speech/ui/article_window.clj +++ b/src/more_speech/ui/article_window.clj @@ -1,6 +1,6 @@ (ns more-speech.ui.article-window (:require - [more-speech.ui.cursor :as text] + [more-speech.ui.cursor :as cursor] [more-speech.content.article :as a] [more-speech.ui.widget :refer [widget]] [more-speech.ui.button :refer [map->button @@ -16,59 +16,66 @@ (defrecord article-window [x y w h page-up page-down display-position] widget (setup-widget [widget state] - (assoc widget :display-position 0 - :page-up (map->button {:x (+ x 20) :y (+ y h -30) :h 20 :w 20 - :left-down scroll-up - :left-held scroll-up - :draw up-arrow}) - :page-down (map->button {:x (+ x w -20) :y (+ y h -30) :h 20 :w 20 - :left-down scroll-down - :left-held scroll-down - :draw down-arrow}) - )) + (let [scroll-up (partial scroll-up (:path widget)) + scroll-down (partial scroll-down (:path widget))] + (assoc widget :display-position 0 + :page-up (map->button {:x (+ x 20) :y (+ y h -30) :h 20 :w 20 + :left-down scroll-up + :left-held scroll-up + :draw up-arrow}) + :page-down (map->button {:x (+ x w -20) :y (+ y h -30) :h 20 :w 20 + :left-down scroll-down + :left-held scroll-down + :draw down-arrow}) + ))) (update-widget [widget state] state) (draw-widget [widget state] - (draw-article-window (:application state) widget)) + (draw-article-window state widget)) ) -(defn- scroll-up [button state] - (let [button-path (:path button) - parent-path (drop-last button-path) - article-window (get-in state parent-path) +(defn- scroll-up [widget-path button state] + (let [article-window (get-in state widget-path) articles (get-in state [:application :chronological-text-events]) display-position (:display-position article-window) display-position (min (count articles) (inc display-position)) article-window (assoc article-window :display-position display-position) - state (assoc-in state parent-path article-window)] + state (assoc-in state widget-path article-window)] state)) -(defn- scroll-down [button state] - (let [button-path (:path button) - parent-path (drop-last button-path) - article-window (get-in state parent-path) +(defn- scroll-down [widget-path button state] + (let [article-window (get-in state widget-path) display-position (:display-position article-window) display-position (max 0 (dec display-position)) article-window (assoc article-window :display-position display-position) - state (assoc-in state parent-path article-window)] + state (assoc-in state widget-path article-window)] state)) +(defn- open-button [cursor] + (cursor/draw-text cursor "+")) + +(defn- null-button [cursor] + (cursor/draw-text cursor " ")) + (defn draw-article [window cursor article] (let [g (:graphics cursor)] (g/text-align g [:left]) (g/fill g [0 0 0]) - (text/render cursor window (a/markup-article article))) + (cursor/render cursor window (a/markup-article article) + {:open-button open-button + :null-button null-button})) ) -(defn draw-articles [application window] - (let [g (:graphics application) +(defn draw-articles [state window] + (let [application (:application state) + g (:graphics application) nicknames (:nicknames application) article-map (:text-event-map application) articles (:chronological-text-events application) display-position (:display-position window) articles (drop display-position articles) articles (take 19 articles)] - (loop [cursor (text/->cursor g 0 (g/line-height g) 5) + (loop [cursor (cursor/->cursor g 0 (g/line-height g) 5) articles articles] (if (empty? articles) cursor @@ -81,8 +88,9 @@ (recur (draw-article window cursor article) (rest articles))))))) -(defn draw-article-window [application window] - (let [g (:graphics application)] +(defn draw-article-window [state window] + (let [application (:application state) + g (:graphics application)] (g/with-translation g [(:x window) (:y window)] (fn [g] @@ -90,5 +98,5 @@ (g/stroke-weight g 2) (g/fill g [255 255 255]) (g/rect g [0 0 (:w window) (:h window)]) - (draw-articles application window)) + (draw-articles state window)) ))) diff --git a/src/more_speech/ui/cursor.clj b/src/more_speech/ui/cursor.clj index 1496c64..5c4e9ad 100644 --- a/src/more_speech/ui/cursor.clj +++ b/src/more_speech/ui/cursor.clj @@ -52,28 +52,35 @@ cursor (recur (draw-line cursor (first lines)) (rest lines))))) -(defn render [cursor window markup] - (let [g (:graphics cursor) - {:keys [bold regular]} (:fonts g)] - (loop [cursor cursor - markup markup] - (cond - (empty? markup) cursor - (= :bold (first markup)) (do (g/text-font g bold) - (recur cursor (rest markup))) - (= :regular (first markup)) (do (g/text-font g regular) - (recur cursor (rest markup))) - (= :pos (first markup)) (recur (set-pos cursor (second markup)) - (drop 2 markup)) - (= :new-line (first markup)) (recur (-> cursor (set-x 0) (new-lines 1)) - (rest markup)) - (= :line (first markup)) (do (g/stroke-weight g 1) - (g/line g [0 (:y cursor) (:w window) (:y cursor)]) - (recur cursor (rest markup))) - (= :multi-line (first markup)) (recur (draw-multi-line cursor (second markup)) - (drop 2 markup)) - (string? (first markup)) (recur (draw-text cursor (first markup)) - (rest markup)) - :else (recur (draw-text cursor (.toString (first markup))) - (rest markup))) - ))) \ No newline at end of file +(defn render + ([cursor window markup] + (render cursor window markup {})) + + ([cursor window markup artifacts] + (let [g (:graphics cursor) + {:keys [bold regular]} (:fonts g)] + (loop [cursor cursor + markup markup] + (cond + (empty? markup) cursor + (= :bold (first markup)) (do (g/text-font g bold) + (recur cursor (rest markup))) + (= :regular (first markup)) (do (g/text-font g regular) + (recur cursor (rest markup))) + (= :pos (first markup)) (recur (set-pos cursor (second markup)) + (drop 2 markup)) + (= :new-line (first markup)) (recur (-> cursor (set-x 0) (new-lines 1)) + (rest markup)) + (= :line (first markup)) (do (g/stroke-weight g 1) + (g/line g [0 (:y cursor) (:w window) (:y cursor)]) + (recur cursor (rest markup))) + (= :multi-line (first markup)) (recur (draw-multi-line cursor (second markup)) + (drop 2 markup)) + (keyword? (first markup)) (let [k (first markup) + f (get artifacts k)] + (recur (f cursor) (rest markup))) + (string? (first markup)) (recur (draw-text cursor (first markup)) + (rest markup)) + :else (recur (draw-text cursor (.toString (first markup))) + (rest markup))) + )))) \ No newline at end of file