A proper way to make XMonad.Actions.WindowMenu configurable?

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

A proper way to make XMonad.Actions.WindowMenu configurable?

Michal J.
Hello friendly folks,

I'm a relative newb to both xmonad and Haskell, and for a while I
wrestled with a seemingly simple task -- to make XMonad.Actions.WindowMenu
(from xmonad-contrib) configurable.

Essentially I wanted to go from:

windowMenu :: X ()
windowMenu = ...

to:

windowMenu :: ??? -> X ()
windowMenu actions = ...

so I can customize the actions that pop up. So I can bind different menus
to different key combos.

Now, I documented some of my struggles at Stack Overflow:
https://stackoverflow.com/q/61001342/1177128
and eventually came up with a solution (with help from others!):
https://stackoverflow.com/a/61013887/1177128

But given that grepping the xmonad and xmonad-contrib sources for
":: XConf ->" returns exactly 1 result (spoiler: `runX` in `Core.hs`), I'm
pretty sure I'm holding it wrong:

```haskell
defaultActions :: XConf -> [(String, X ())]
defaultActions = do
    tags <- asks (workspaces . config)
    return ([ ("Cancel menu", return ())
            , ("Close"      , kill)
            , ("Maximize"   , withFocused $ \w -> sendMessage $ maximizeRestore w)
            , ("Minimize"   , withFocused $ \w -> minimizeWindow w)
            ] ++
            [ ("Move to " ++ tag, windows $ W.shift tag) | tag <- tags ])

windowMenu' :: (XConf -> [(String, X ())]) -> X ()
windowMenu' actions = withFocused $ \w -> do
    acts <- asks actions
    Rectangle x y wh ht <- getSize w
    Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
    let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
        originFractY = (fi y - fi sy + fi ht / 2) / fi sht
        gsConfig = (buildDefaultGSConfig colorizer)
                    { gs_originFractX = originFractX
                    , gs_originFractY = originFractY }
    runSelectedAction gsConfig acts

-- now it composes well, and I can pass in my own `actions` to `windowMenu`
windowMenu = windowMenu' defaultActions
```

Can anyone well versed in the idiosyncrasies of Haskell and XMonad please
point out what would be the proper -- nay, canonical -- solution if it were
done by someone who actually knows what they're doing?

It'd be greatly appreciated. :-)

Cheers,

Michal

PS: If the refactor seems like I really have no clue what's going on...
    well... I don't. And if you have a pointer to some text I should
        read to understand all this better, I'm all ears.
_______________________________________________
xmonad mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad
Reply | Threaded
Open this post in threaded view
|

Re: A proper way to make XMonad.Actions.WindowMenu configurable?

Brandon Allbery
For what it's worth, you normally get the XConf via `ask` (Control.Monad.Reader) instead of passing it around. The primary exception is in ManageHooks, which have a different Reader on top and you must use `liftX` first to get at the other one.

Your own actions would not normally be part of the XConf or XConfig, but a separate type which you would pass around. Compare something like XMonad.Prompt and its `XPConfig` type.

On Fri, Apr 3, 2020 at 10:46 AM Michal J. <[hidden email]> wrote:
Hello friendly folks,

I'm a relative newb to both xmonad and Haskell, and for a while I
wrestled with a seemingly simple task -- to make XMonad.Actions.WindowMenu
(from xmonad-contrib) configurable.

Essentially I wanted to go from:

windowMenu :: X ()
windowMenu = ...

to:

windowMenu :: ??? -> X ()
windowMenu actions = ...

so I can customize the actions that pop up. So I can bind different menus
to different key combos.

Now, I documented some of my struggles at Stack Overflow:
https://stackoverflow.com/q/61001342/1177128
and eventually came up with a solution (with help from others!):
https://stackoverflow.com/a/61013887/1177128

But given that grepping the xmonad and xmonad-contrib sources for
":: XConf ->" returns exactly 1 result (spoiler: `runX` in `Core.hs`), I'm
pretty sure I'm holding it wrong:

```haskell
defaultActions :: XConf -> [(String, X ())]
defaultActions = do
    tags <- asks (workspaces . config)
    return ([ ("Cancel menu", return ())
            , ("Close"      , kill)
            , ("Maximize"   , withFocused $ \w -> sendMessage $ maximizeRestore w)
            , ("Minimize"   , withFocused $ \w -> minimizeWindow w)
            ] ++
            [ ("Move to " ++ tag, windows $ W.shift tag) | tag <- tags ])

windowMenu' :: (XConf -> [(String, X ())]) -> X ()
windowMenu' actions = withFocused $ \w -> do
    acts <- asks actions
    Rectangle x y wh ht <- getSize w
    Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
    let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
        originFractY = (fi y - fi sy + fi ht / 2) / fi sht
        gsConfig = (buildDefaultGSConfig colorizer)
                    { gs_originFractX = originFractX
                    , gs_originFractY = originFractY }
    runSelectedAction gsConfig acts

-- now it composes well, and I can pass in my own `actions` to `windowMenu`
windowMenu = windowMenu' defaultActions
```

Can anyone well versed in the idiosyncrasies of Haskell and XMonad please
point out what would be the proper -- nay, canonical -- solution if it were
done by someone who actually knows what they're doing?

It'd be greatly appreciated. :-)

Cheers,

Michal

PS: If the refactor seems like I really have no clue what's going on...
    well... I don't. And if you have a pointer to some text I should
        read to understand all this better, I'm all ears.
_______________________________________________
xmonad mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad


--
brandon s allbery kf8nh

_______________________________________________
xmonad mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad
Reply | Threaded
Open this post in threaded view
|

Re: A proper way to make XMonad.Actions.WindowMenu configurable?

Michal J.
Hi Brandon,

>    For what it's worth, you normally get the XConf via `ask`
>    (Control.Monad.Reader) instead of passing it around. The primary
>    exception is in ManageHooks, which have a different Reader on top and
>    you must use `liftX` first to get at the other one.

Thanks for the clarification.

I think it'll take me a bit more work (after finishing the "wikibook"
about Haskell I started yesterday) to truly understand all this.

I mean, I still don't understand how one goes from `X ()` to bunch of
other -- more specific -- subtypes, but at this point I have a feeling
that a few more (tens of) hours of study will get me there.

>    Your own actions would not normally be part of the XConf or XConfig,
>    but a separate type which you would pass around.

Yeah, another kind soul at SO pointed out my folly:
https://stackoverflow.com/a/61019840/1177128
and basically told me how to convert my (apparently "bonkers") solution
into something that's more understandable (and ~matches what you said):

```haskell
-- details at the abovementioned link
defaultActions :: X [(String, X ())]
windowMenu' :: [(String, X ())] -> X ()

windowMenu :: X ()
windowMenu = defaultActions >>= windowMenu'
```

> Compare something like XMonad.Prompt and its `XPConfig` type.

Thanks for the pointer. :-)

Cheers,

Michal
_______________________________________________
xmonad mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/xmonad