Skip to content

Commit

Permalink
Merge pull request #27 from BrookJeynes/add-color-map-layout-node
Browse files Browse the repository at this point in the history
feat: Add color map layout node
  • Loading branch information
cfoust authored Oct 18, 2024
2 parents b385923 + 570811f commit 5f67f45
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 11 deletions.
19 changes: 19 additions & 0 deletions pkg/cy/api/layout_test.janet
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,25 @@

(expect-error (layout/set layout)))

(test "color-map"
(def layout
(layout/new
(color-map
@{}
(attach))))

(layout/set layout)
(assert (deep= (layout/get) layout)))

(test "invalid color-map"
(def layout
(layout/new
(color-map
""
(attach))))

(expect-error (layout/set layout)))

(test "tab actions"
(action/new-tab)
(action/new-tab)
Expand Down
13 changes: 12 additions & 1 deletion pkg/cy/boot/layout.janet
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ For example:
(or
(layout/type? :margins node)
(layout/type? :bar node)
(layout/type? :borders node)) @[[:node]]
(layout/type? :borders node)
(layout/type? :color-map node)) @[[:node]]
@[]))

(defn
Expand Down Expand Up @@ -168,6 +169,14 @@ For example:
:text text
:bottom bottom})

(defn
layout/color-map
```Convenience function for creating a new :color-map node.```
[map node]
{:type :color-map
:map map
:node node})

(defmacro
layout/new
```Macro for quickly creating layouts. layout/new replaces shorthand versions of node creation functions with their longform versions and also includes a few abbreviations that do not exist elsewhere in the API.
Expand All @@ -184,6 +193,7 @@ Supported short forms:
* tab: A :tab inside of a :tabs node.
* active-tab: A :tab with :active=true inside of a :tabs node.
* bar: A :bar node.
* color-map: A :color-map node.
See [the layouts chapter](/layouts.md#api) for more information.
```
Expand All @@ -209,6 +219,7 @@ See [the layouts chapter](/layouts.md#api) for more information.
(def tab ,layout/tab)
(def active-tab ,active-tab)
(def bar ,layout/bar)
(def color-map ,layout/color-map)
,body))

(defn
Expand Down
49 changes: 49 additions & 0 deletions pkg/cy/stories.go
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,55 @@ func init() {
return screen, err
}, stories.Config{})

stories.Register("layout/colormap", func(ctx context.Context) (
mux.Screen,
error,
) {
_, client, screen, err := createStory(ctx)
err = client.execute(`
(def cmd1 (shell/new))
(layout/set (layout/new
(color-map
(fn [layout]
(def node (layout/attach-id layout))
(if
(nil? node) @{"0" "#ff0000"}
@{"0" "#f00000"}))
(attach :id cmd1))))
(def cmd1 (shell/new))
(def cmd2 (shell/new))
(defn
theme [layout]
(def node (layout/attach-id layout))
(if
(nil? node) @{"0" "#ff0000"}
@{"0" "#0000ff"}))
(layout/set (layout/new
(split
(color-map
theme
(attach :id cmd1))
(color-map
theme
(pane :id cmd2)))))
`)
return screen, err
}, stories.Config{
Input: []interface{}{
stories.Wait(stories.Some),
stories.Type("ctrl+a", "L"),
stories.Wait(stories.Some),
stories.Type("ctrl+a", "H"),
stories.Wait(stories.Some),
stories.Type("ctrl+a", "L"),
stories.Wait(stories.Some),
stories.Type("ctrl+a", "H"),
stories.Wait(stories.Some),
},
})

stories.Register("layout/bar/bad", func(ctx context.Context) (
mux.Screen,
error,
Expand Down
132 changes: 132 additions & 0 deletions pkg/layout/colormap/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package colormap

import (
"context"

"github.com/cfoust/cy/pkg/geom"
"github.com/cfoust/cy/pkg/geom/tty"
L "github.com/cfoust/cy/pkg/layout"
"github.com/cfoust/cy/pkg/layout/prop"
"github.com/cfoust/cy/pkg/mux"
"github.com/cfoust/cy/pkg/taro"

"github.com/sasha-s/go-deadlock"
)

type ColorMap struct {
*L.Computable
deadlock.RWMutex
*mux.UpdatePublisher
render *taro.Renderer
screen mux.Screen
size geom.Size
config L.ColorMapType
}

var _ mux.Screen = (*ColorMap)(nil)
var _ L.Reusable = (*ColorMap)(nil)

func (l *ColorMap) Apply(node L.NodeType) (bool, error) {
config, ok := node.(L.ColorMapType)
if !ok {
return false, nil
}

l.Lock()
defer l.Unlock()

l.config = config

layout := L.New(config.Node)
for _, prop := range []prop.Presettable{
config.Map,
} {
prop.SetLogger(l.Logger)
prop.Preset(
l.Ctx(),
l.Context.Context(),
&layout,
)
prop.ClearCache()
}

return true, nil
}

func (l *ColorMap) Kill() {
l.RLock()
screen := l.screen
l.RUnlock()

screen.Kill()
}

func (l *ColorMap) State() *tty.State {
l.Lock()
defer l.Unlock()

var (
size = l.size
config = l.config
)

innerState := l.screen.State()
state := tty.New(size)
tty.Copy(geom.Vec2{}, state, innerState)

if value, ok := config.Map.GetPreset(); ok {
value.Apply(state.Image)
}

return state
}

func (l *ColorMap) Send(msg mux.Msg) {
l.RLock()
l.RUnlock()
l.screen.Send(msg)
}

func (l *ColorMap) Size() geom.Size {
l.RLock()
defer l.RUnlock()
return l.size
}

func (l *ColorMap) poll(ctx context.Context) {
updates := l.screen.Subscribe(ctx)

for {
select {
case <-ctx.Done():
return
case event := <-updates.Recv():
if _, ok := event.(L.NodeChangeEvent); ok {
continue
}
l.Publish(event)
}
}
}

func (l *ColorMap) Resize(size geom.Size) error {
l.Lock()
l.size = size
l.Unlock()
return l.screen.Resize(size)
}

func New(ctx context.Context, screen mux.Screen) *ColorMap {
c := L.NewComputable(ctx)
colormap := &ColorMap{
Computable: c,
UpdatePublisher: mux.NewPublisher(),
size: geom.DEFAULT_SIZE,
screen: screen,
render: taro.NewRenderer(),
}

go colormap.poll(colormap.Ctx())

return colormap
}
40 changes: 40 additions & 0 deletions pkg/layout/engine/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
L "github.com/cfoust/cy/pkg/layout"
"github.com/cfoust/cy/pkg/layout/bar"
"github.com/cfoust/cy/pkg/layout/borders"
"github.com/cfoust/cy/pkg/layout/colormap"
"github.com/cfoust/cy/pkg/layout/margins"
"github.com/cfoust/cy/pkg/layout/pane"
"github.com/cfoust/cy/pkg/layout/split"
Expand Down Expand Up @@ -42,6 +43,8 @@ func (l *LayoutEngine) createNode(
err = l.createTabs(node, config)
case L.BarType:
err = l.createBar(node, config)
case L.ColorMapType:
err = l.createColorMap(node, config)
default:
err = fmt.Errorf("unimplemented screen")
}
Expand Down Expand Up @@ -155,6 +158,13 @@ func (l *LayoutEngine) updateNode(
Node: current.Children[0],
},
)
case L.ColorMapType:
updates = append(updates,
updateNode{
Config: node.Node,
Node: current.Children[0],
},
)
}

// If any of the node's children cannot be reused, we need to remake
Expand Down Expand Up @@ -314,6 +324,28 @@ func (l *LayoutEngine) createBar(
return nil
}

func (l *LayoutEngine) createColorMap(
node *screenNode,
config L.ColorMapType,
) error {
innerNode, err := l.createNode(
node.Ctx(),
config.Node,
)
if err != nil {
return err
}

colormap := colormap.New(
node.Ctx(),
innerNode.Screen,
)

node.Screen = colormap
node.Children = []*screenNode{innerNode}
return nil
}

// applyNodeChange replaces the configuration of the target node with
// newConfig. This is only used to allow nodes to change their own
// configurations in response to user input (for now, just mouse events.)
Expand Down Expand Up @@ -380,6 +412,14 @@ func applyNodeChange(
newConfig,
)
return currentConfig
case L.ColorMapType:
currentConfig.Node = applyNodeChange(
current.Children[0],
target,
currentConfig.Node,
newConfig,
)
return currentConfig
}

return currentConfig
Expand Down
Loading

0 comments on commit 5f67f45

Please sign in to comment.