diff --git a/assets/theme.css b/assets/theme.css index ccf8b17..e12754f 100644 --- a/assets/theme.css +++ b/assets/theme.css @@ -5,33 +5,33 @@ } */ -.toggle, .dialogButton, .titlebar, .titleBar, .windowIcon, .fileBarCloseButton, -.newFileButton, .header, .paned, .panel, .tabs, .frame { +.metabrush .toggle, .metabrush .dialogButton, .metabrush .titlebar, +.metabrush .windowIcon, .metabrush .fileBarCloseButton, +.metabrush .newFileButton, .metabrush .header, +.metabrush .paned, .metabrush .panel, .metabrush .tabs, .metabrush .frame { all: unset; } -windowcontrols { +.metabrush windowcontrols { all: unset; } -.reorderable-page { +.metabrush .reorderable-page { all: unset; } -.menu * { +.metabrush .headerMenu * { all: unset; } -.frame > * { +.metabrush .frame > * { all: unset; } - -.notebook { +.metabrush .notebook { all: unset; } - @import url("colours.css"); /* Colours parsed by application */ @@ -100,24 +100,24 @@ windowcontrols { .pointSelected { color: @pointSelected; } -.viewport { +.metabrush .viewport { background-color: @base; color: @base; min-width: 120px; min-height: 90px; } -.viewportScrollbar { +.metabrush .viewportScrollbar { background-color: @scrollbar; color: @scrollbar; margin: 4px; min-width: 8px; min-height: 8px; } -.tabScrollbar { +.metabrush .tabScrollbar { background-color: @scrollbar; color: @scrollbar; } -.ruler { +.metabrush .ruler { background-color: @ruler; color: @ruler; min-width: 16px; @@ -159,7 +159,7 @@ tooltip { } -.window, .dialog { +.metabrush .window, .metabrush .dialog { border-radius: 0px; border-color: @border; box-shadow: @@ -172,54 +172,54 @@ tooltip { /* Basic text colour */ /* Basic text font */ -.text { +.metabrush .text { font-family: "Lato", "Roboto", "Helvetica", sans-serif; } /* Monospace font */ -.monospace { +.metabrush .monospace { font-family: "Fira Code", "Inconsolata", "Courier", "Courier New", monospace; } /* High-constrast text colour */ -.contrast { +.metabrush .contrast { color: @contrast; } /* Active (highlighting) colour */ -.highlight { +.metabrush .highlight { color: @highlight; } /* Logo area */ -.logo { +.metabrush .logo { margin-left: 4px; min-width: 28px; } /* Logo base colour */ -.logo_base { +.metabrush .logo_base { color: @highlight; } /* Logo highlight colour */ -.logo_highlight { +.metabrush .logo_highlight { color: @splash; } /* Rulers */ -.leftRuler { +.metabrush .leftRuler { border-right: 1px solid @rulerBorder; min-width: 16px; } -.topRuler { +.metabrush .topRuler { border-bottom: 1px solid @rulerBorder; min-height: 16px; } -.rulerCorner { +.metabrush .rulerCorner { min-width: 8px; min-height: 8px; border-bottom: 1px solid @rulerBorder; @@ -227,44 +227,44 @@ tooltip { } /* Cursor colour */ -.cursor { +.metabrush .cursor { color: @cursor; } /* Bézier path point colour */ -.point { +.metabrush .point { color: @pathPoint; } /* Bézier control point colour */ -.control { +.metabrush .control { color: @controlPoint; } /* Title bar */ -.titleBar { +.metabrush .titleBar { min-height: 24px; font-size: 12px; background-color: @bg; } /* -.titleBar > * :hover { +.metabrush .titleBar > * :hover { background-color: rgb(72,70,61); } */ -.title { +.metabrush .title { border-top: 2px solid @bg; } -.dialog { +.metabrush .dialog { border: 1px solid @border; border-radius: 6px; } /* dialog button */ -.dialogButton { +.metabrush .dialogButton { background-color: @active; border: 1px solid @border; border-radius: 4px; @@ -272,11 +272,11 @@ tooltip { padding: 2px 10px 2px 10px; } -.dialogButton:hover { +.metabrush .dialogButton:hover { border-color: @plain; } -.dialogButton:active, .dialogButton:checked { +.metabrush .dialogButton:active, .metabrush .dialogButton:checked { color: @active; border-color: @border; background-color: @highlight; @@ -284,31 +284,31 @@ tooltip { /* Menu bar */ -.menu label { +.metabrush .headerMenu label { color:@plain; } -.menu :disabled { +.metabrush .headerMenu :disabled { color: @disabled; } -.menu :focus-within { +.metabrush .headerMenu :focus-within { border-color: @plain; } -.menu :focus{ +.metabrush .headerMenu :focus{ border-color: @active; background-color: @bg; } -.menu item { +.metabrush .headerMenu item { padding-left: 8px; padding-right: 8px; color: @plain; border-top: 2px solid @bg; } -.menu item > * :hover { +.metabrush .headerMenu item > * :hover { border-color: @plain; background-color: @active; } @@ -318,15 +318,15 @@ tooltip { "The visible part of the popover can have a shadow. To specify it in CSS, set the box-shadow of the contents node." */ -.menu item > popover > contents { +.metabrush .headerMenu item > popover > contents { box-shadow: 2px 3px 3px rgba(0,0,0,0.5) , -2px 3px 3px rgba(0,0,0,0.5) , 0px 4px 3px rgba(0,0,0,0.5); } /* Look at the CSS with the GTK inspector to figure this out... */ -.menu item > popover > contents > scrolledwindow > viewport > stack > box > box > modelbutton, -.menu item > popover > contents > scrolledwindow > viewport > stack > box > box > box > box > modelbutton +.metabrush .headerMenu item > popover > contents > scrolledwindow > viewport > stack > box > box > modelbutton, +.metabrush .headerMenu item > popover > contents > scrolledwindow > viewport > stack > box > box > box > box > modelbutton { background-color: @bg; color: @active; @@ -335,46 +335,95 @@ To specify it in CSS, set the box-shadow of the contents node." padding: 6px 10px 6px 10px; } -.menu item > popover > contents > scrolledwindow > viewport > stack > box > box > box > separator, -.menu item > popover > contents > scrolledwindow > viewport > stack > box > box > box > separator :hover { +.metabrush .headerMenu item > popover > contents > scrolledwindow > viewport > stack > box > box > box > separator, +.metabrush .headerMenu item > popover > contents > scrolledwindow > viewport > stack > box > box > box > separator :hover { background-color: @active; padding: 1px 0px 0px 1px; } -.menu item > popover > contents > scrolledwindow > viewport > stack > box > box > modelbutton > accelerator, -.menu item > popover > contents > scrolledwindow > viewport > stack > box > box > box > box > modelbutton > accelerator { +.metabrush .headerMenu item > popover > contents > scrolledwindow > viewport > stack > box > box > modelbutton > accelerator, +.metabrush .headerMenu item > popover > contents > scrolledwindow > viewport > stack > box > box > box > box > modelbutton > accelerator { font-size: 10px; color: @shortcutKey; padding-left: 10px; } -.windowIcon { +.metabrush .listViewPane :selected { + background-color: unset; + font-weight: bold; + color: black; +} + +/* Stroke popover menu */ +/* TODO: refactor with menu */ +.metabrush .strokeMenu > * { + background-color: @bg; +} + +.metabrush .strokeMenu contents { + border: unset; + border-radius: 0px; + margin: 0px; + padding: 0px; + border: 1px solid black; +} + +.metabrush .strokeMenu .flat { + border-left: 2px solid @bg; + border-top: 0px; +} + +.metabrush .strokeMenu :selected { + color: @plain; + background-color: @active; + font-weight: normal; + border-left: 2px solid @plain; + border-radius: 0px; +} + +.metabrush .strokeMenu separator { + background-color: @contrast; + margin: 0px; +} + +.metabrush .strokeMenu .separator { + color: black; + background-color: @contrast; + font-weight: normal; + opacity: 1; + margin: 4px 10px 4px 10px; + padding: 0px 6px 0px 6px; + border: 0px solid black; + border-radius: 2px; +} + +.metabrush .windowIcon { min-width: 24px; } -.windowIcon:hover { +.metabrush .windowIcon:hover { background-color: @windowButtonHover; } -.windowIcon:active, .windowIcon:checked { +.metabrush .windowIcon:active, .metabrush .windowIcon:checked { background-color: @windowButtonActive; } -.closeWindowIcon:hover { +.metabrush .closeWindowIcon:hover { background-color: @closeButtonHover; } -.closeWindowIcon:active, .closeWindowIcon:checked { +.metabrush .closeWindowIcon:active, .metabrush .closeWindowIcon:checked { background-color: @closeButtonActive; } /* Tool bar */ -.toolBar { +.metabrush .toolBar { min-width: 32px; margin-top: 28px; } -.toolBarSeparator { +.metabrush .toolBarSeparator { min-height: 2px; margin-left: 10px; margin-right: 10px; @@ -383,7 +432,7 @@ To specify it in CSS, set the box-shadow of the contents node." background-color: @active; } -.toolItem { +.metabrush .toolItem { border-left: 2px solid @bg; min-height: 40px; min-width: 40px; @@ -393,42 +442,42 @@ To specify it in CSS, set the box-shadow of the contents node." padding-right: 3px; } -.toolItem:hover { +.metabrush .toolItem:hover { border-color: @active; } -.toolItem:active, .toolItem:checked { +.metabrush .toolItem:active, .metabrush .toolItem:checked { border-color: @base; background-color: @active; } /* File bar */ -.fileBar { +.metabrush .fileBar { min-height: 24px; font-size: 12px; margin-top: 2px; } -.fileBarTab { +.metabrush .fileBarTab { border-top: 2px solid @bg; } -.fileBarTab:hover { +.metabrush .fileBarTab:hover { border-color: @active; } -.fileBarTab:active, .fileBarTab:checked { +.metabrush .fileBarTab:active, .metabrush .fileBarTab:checked { border-color: @base; background-color: @active; } -.fileBarTabButton { +.metabrush .fileBarTabButton { padding-left: 8px; padding-right: 2px; margin: 0px; } -.fileBarCloseButton { +.metabrush .fileBarCloseButton { min-width: 10px; min-height: 22px; padding-left: 1px; @@ -437,15 +486,15 @@ To specify it in CSS, set the box-shadow of the contents node." color: @plain; } -.fileBarCloseButton:hover { +.metabrush .fileBarCloseButton:hover { color: @closeButtonHover; } -.fileBarCloseButton:active, .fileBarCloseButton:checked { +.metabrush .fileBarCloseButton:active, .metabrush .fileBarCloseButton:checked { color: @closeButtonActive; } -.newFileButton { +.metabrush .newFileButton { color: @newFileButton; font-size: 16px; font-weight: bold; @@ -455,55 +504,55 @@ To specify it in CSS, set the box-shadow of the contents node." border-left: 1px solid @bg; } -.newFileButton:hover, .newFileButton:active, newFileButton:checked { +.metabrush .newFileButton:hover, .metabrush .newFileButton:active, .metabrush newFileButton:checked { color: @newFileButtonActive; } -.newFileButton:active, .newFileButton:checked { +.metabrush .newFileButton:active, .metabrush .newFileButton:checked { border-color: @newFileButtonActive; } /* Panels */ -.panels { +.metabrush .panels { min-width: 120px; font-size: 12px; } -.panels tab { +.metabrush .panels tab { padding-left: 6px; padding-right: 6px; border-top: 2px solid @bg; } -.panels tab:hover { +.metabrush .panels tab:hover { border-color: @active; } -.panels tab:active, .panels tab:checked { +.metabrush .panels tab:active, .metabrush .panels tab:checked { background-color: @active; border-color: rgb(234,223,204); } -.panel { +.metabrush .panel { background-color: @active; min-height: 20px; } /* Info bar */ -.infoBar { +.metabrush .infoBar { min-height: 40px; font-size: 10px; } -.infoBarInfo { +.metabrush .infoBarInfo { margin-left: -4px; padding-right: 16px; } /* Stroke hierarchy layers */ -row { +.metabrush row { border-top: 0px; border-bottom: 0px; margin-top: -2px; @@ -511,18 +560,12 @@ row { } /* Slightly hacky way to align layers and groups */ -indent { +.metabrush indent { margin-left: 4px; margin-right: 4px; } -:selected { - background-color: rgba(255,255,255,0); - font-weight: bold; - color: black; -} - -.layer-item, .brush-item { +.metabrush .layer-item, .metabrush .brush-item { color: @plain; background-color: @active; border: 0px solid @bg; @@ -536,22 +579,22 @@ indent { padding-left: 3px; } -:selected .layer-item, :selected .brush-item { +.metabrush :selected .layer-item, .metabrush :selected .brush-item { color: black; background-color: @contrast; border: 0px solid @contrast; } /* Add "drop here" areas when a drag has been initiated */ -.dragging-item .layer-item { +.metabrush .dragging-item .layer-item { } /* Style when dragging over an item */ -.drag-over.layer-item { +.metabrush .drag-over.layer-item { } /* Style when dragging layer item over the top part of an item */ -.dragging-item .drag-top.layer-item { +.metabrush .dragging-item .drag-top.layer-item { border-top: 2px solid @highlight; margin-top: -2px; box-shadow: @@ -559,7 +602,7 @@ indent { } /* Style when dragging layer item over the bottom part of an item */ -.dragging-item .drag-bot.layer-item { +.metabrush .dragging-item .drag-bot.layer-item { border-bottom: 2px solid @highlight; margin-bottom: -2px; box-shadow: @@ -567,7 +610,7 @@ indent { } /* Style when dragging brush over an item */ -.dragging-brush .drag-top.layer-item, .dragging-brush .drag-bot.layer-item { +.metabrush .dragging-brush .drag-top.layer-item, .dragging-brush .drag-bot.layer-item { border-top: 2px solid @brushStroke; border-bottom: 2px solid @brushStroke; border-left: 0px; @@ -578,7 +621,7 @@ indent { } /* Style for item being dragged */ -.dragged.layer-item { +.metabrush .dragged.layer-item { background-color: @bg; transition: background-color 0.4s ease-in-out; diff --git a/src/app/MetaBrush/Application/Action.hs b/src/app/MetaBrush/Application/Action.hs index 44ca0e9..3b9c675 100644 --- a/src/app/MetaBrush/Application/Action.hs +++ b/src/app/MetaBrush/Application/Action.hs @@ -686,6 +686,34 @@ instance HandleAction Delete where -- TODO: handle deletion of layers by checking the current focus. _ -> pure () +------------------ +-- Delete layer -- +------------------ + +data DeleteLayer = DeleteLayer !MetaBrush.Layer.Layer + deriving stock Show + +instance HandleAction MetaBrush.Application.Action.DeleteLayer where + handleAction + uiElts + vars@( Variables { toolTVar, modeTVar } ) + _ = + return () + +--------------- +-- New group -- +--------------- + +data GroupPosition = GroupAbove | GroupBelow | GroupContaining + deriving stock Show + +data NewGroup = NewGroup !GroupPosition !MetaBrush.Layer.Layer + deriving stock Show + +instance HandleAction NewGroup where + handleAction ( UIElements { viewport = Viewport {..} } ) ( Variables { redrawStrokesTVar, showGuidesTVar } ) ( NewGroup pos lay ) = do + return () + ------------------- -- Toggle guides -- ------------------- diff --git a/src/app/MetaBrush/Application/Action.hs-boot b/src/app/MetaBrush/Application/Action.hs-boot index 2eea202..e74cf5b 100644 --- a/src/app/MetaBrush/Application/Action.hs-boot +++ b/src/app/MetaBrush/Application/Action.hs-boot @@ -7,6 +7,10 @@ import Data.Word -- gi-gtk import qualified GI.Gtk as GTK +-- text +import Data.Text + ( Text ) + -- MetaBrush import Math.Linear ( ℝ(..), T(..) ) @@ -15,9 +19,12 @@ import MetaBrush.UI.Viewport ( Ruler(..) ) import MetaBrush.Unique ( Unique ) +import MetaBrush.Layer (Layer) -------------------------------------------------------------------------------- +actionPrefix :: ActionName -> Text + class HandleAction action where handleAction :: UIElements -> Variables -> action -> IO () @@ -38,6 +45,9 @@ instance HandleAction Save data SaveAs = SaveAs instance HandleAction SaveAs +data Export = Export +instance HandleAction Export + data Close = CloseActive | CloseThis @@ -76,6 +86,13 @@ instance HandleAction Duplicate data Delete = Delete instance HandleAction Delete +data DeleteLayer = DeleteLayer !Layer +instance HandleAction DeleteLayer + +data GroupPosition = GroupAbove | GroupBelow | GroupContaining +data NewGroup = NewGroup !GroupPosition !Layer +instance HandleAction NewGroup + data ToggleGuides = ToggleGuides instance HandleAction ToggleGuides @@ -85,6 +102,9 @@ instance HandleAction Confirm data About = About instance HandleAction About +data OpenPrefs = OpenPrefs +instance HandleAction OpenPrefs + data MouseMove = MouseMove !( ℝ 2 ) instance HandleAction MouseMove diff --git a/src/app/MetaBrush/UI/Menu.hs b/src/app/MetaBrush/UI/Menu.hs index 48f315c..2074b45 100644 --- a/src/app/MetaBrush/UI/Menu.hs +++ b/src/app/MetaBrush/UI/Menu.hs @@ -39,8 +39,7 @@ import qualified Data.HashSet as HashSet ( fromList, toMap ) -- MetaBrush -import MetaBrush.Application.Action - hiding ( save, saveAs ) +import {-# SOURCE #-} MetaBrush.Application.Action import MetaBrush.Application.Context import MetaBrush.Asset.Colours ( Colours ) @@ -48,6 +47,8 @@ import MetaBrush.Asset.WindowIcons ( drawMinimise, drawRestoreDown, drawMaximise, drawClose ) import MetaBrush.GTK.Util ( widgetAddClass, widgetAddClasses ) +import MetaBrush.Layer + ( Layer(..) ) -------------------------------------------------------------------------------- -- Types for describing menu items. @@ -92,6 +93,11 @@ menuActionNames = HashSet.fromList , WinAction "about" -- preferences , WinAction "prefs" + -- stroke actions + , WinAction "newGroupAbove" + , WinAction "newGroupBelow" + , WinAction "newGroupContaining" + , WinAction "deleteLayer" ] createMenuActions :: IO ( HashMap ActionName GIO.SimpleAction ) @@ -165,6 +171,18 @@ helpMenuDescription = [ MenuItemDescription "About MetaBrush" ( Just $ WinAction "about", About ) ( Just "question" ) ] +strokeMenuDescription :: Layer -> [ MenuItem ] +strokeMenuDescription lay = + [ case lay of + StrokeLayer {} -> MenuItemDescription "Delete stroke" ( Nothing, DeleteLayer lay) Nothing + GroupLayer {} -> MenuItemDescription "Delete group" ( Nothing, DeleteLayer lay) Nothing + , Section ( Just "New group" ) $ + [ MenuItemDescription "...above" ( Nothing, NewGroup GroupAbove lay ) Nothing + , MenuItemDescription "...below" ( Nothing, NewGroup GroupBelow lay ) Nothing + , MenuItemDescription "...containing" ( Nothing, NewGroup GroupContaining lay ) Nothing + ] + ] + -------------------------------------------------------------------------------- -- Creating a GTK popover menu bar from a menu description. @@ -225,7 +243,7 @@ createMenuBar uiElts@( UIElements { application, window, titleBar } ) vars colou --GTK.windowAddAccelGroup window accelGroup menu <- createMenu uiElts vars menuBar <- GTK.popoverMenuBarNewFromModel ( Just menu ) - widgetAddClasses menuBar [ "menu", "text", "plain" ] + widgetAddClasses menuBar [ "headerMenu", "text", "plain" ] GTK.headerBarPackStart titleBar menuBar -- TODO: this is a bit of a workaround to add hover highlight to top-level menu items. diff --git a/src/app/MetaBrush/UI/Panels.hs b/src/app/MetaBrush/UI/Panels.hs index 3506b17..5dd91cc 100644 --- a/src/app/MetaBrush/UI/Panels.hs +++ b/src/app/MetaBrush/UI/Panels.hs @@ -67,7 +67,7 @@ createPanelBar panelBox = do for_ [ strokesTab, brushesTab, transformTab, historyTab ] \ tab -> do widgetAddClasses tab [ "plain", "text", "panelTab" ] - for_ [ layersScrolledWindow, brushesScrolledWindow ] \ w -> widgetAddClass w "panel" + for_ [ layersScrolledWindow, brushesScrolledWindow ] \ w -> widgetAddClasses w [ "panel", "listViewPane" ] for_ [ transformPanelBox, historyPanelBox ] \ panel -> do widgetAddClass panel "panel" diff --git a/src/app/MetaBrush/UI/StrokeTreeView.hs b/src/app/MetaBrush/UI/StrokeTreeView.hs index ab5e6c6..268b426 100644 --- a/src/app/MetaBrush/UI/StrokeTreeView.hs +++ b/src/app/MetaBrush/UI/StrokeTreeView.hs @@ -89,9 +89,13 @@ import MetaBrush.Brush import MetaBrush.Document import MetaBrush.Document.Diff import MetaBrush.Document.History +import MetaBrush.GTK.Util import MetaBrush.Layer import MetaBrush.Stroke hiding ( Layer(..) ) import MetaBrush.Unique +import MetaBrush.UI.Menu + ( strokeMenuDescription, makeMenu ) + -------------------------------------------------------------------------------- @@ -418,6 +422,9 @@ newLayerView :: UIElements -> Variables -> IO GTK.ListView newLayerView uiElts@( UIElements { window } ) vars = mdo layersListFactory <- GTK.signalListItemFactoryNew + layerMenu <- GIO.menuNew + layerPopover <- GTK.popoverMenuNewFromModel ( Nothing @GIO.Menu ) + widgetAddClasses layerPopover [ "strokeMenu", "text", "plain" ] -- Connect to "setup" signal to create generic widgets for viewing the tree. -- @@ -440,13 +447,36 @@ newLayerView uiElts@( UIElements { window } ) vars = mdo expander <- newLayerViewWidget GTK.listItemSetChild listItem ( Just expander ) - GTK.widgetAddCssClass expander "layer-item" + widgetAddClass expander "layer-item" LayerViewWidget { layerViewLabel = label , layerViewCheckButton = visibleButton } <- getLayerViewWidget expander + ------------------ + -- GestureClick -- + ------------------ + + click <- GTK.gestureClickNew + + void $ GTK.onGestureClickPressed click $ \ clickNo x y -> do + case clickNo of + _ -> do + GTK.widgetUnparent layerPopover + GTK.widgetSetParent layerPopover expander + layer <- getLayerData listItem + GIO.menuRemoveAll layerMenu + makeMenu uiElts vars layerMenu ( strokeMenuDescription layer ) + GTK.popoverMenuSetMenuModel layerPopover ( Just layerMenu ) + rect <- GDK.newZeroRectangle + GDK.setRectangleX rect ( round x ) + GDK.setRectangleY rect ( round y ) + GTK.popoverSetPointingTo layerPopover ( Just rect ) + GTK.popoverPopup layerPopover + + _ -> return () + ---------------------------- -- Visibility CheckButton -- ---------------------------- @@ -516,7 +546,7 @@ newLayerView uiElts@( UIElements { window } ) vars = mdo } val <- GDK.contentProviderNewForValue =<< GIO.toGValue ( GI.HValue dnd_sourceItem ) - GTK.widgetAddCssClass window "dragging-item" + widgetAddClass window "dragging-item" return $ Just val void $ GTK.onDragSourceDragBegin dragSource $ \ _drag -> do @@ -528,7 +558,7 @@ newLayerView uiElts@( UIElements { window } ) vars = mdo -} noPaintable <- GDK.paintableNewEmpty 0 0 GTK.dragSourceSetIcon ?self ( Just noPaintable ) 0 0 - GTK.widgetAddCssClass expander "dragged" + widgetAddClass expander "dragged" -- TODO: add "dragged" class for all descendants as well. void $ GTK.onDragSourceDragCancel dragSource $ \ _drag _reason -> do GTK.widgetRemoveCssClass window "dragging-item" @@ -720,38 +750,39 @@ newLayerView uiElts@( UIElements { window } ) vars = mdo return True void $ GTK.onDropTargetEnter dropTarget $ \ _x y -> do - GTK.widgetAddCssClass expander "drag-over" + widgetAddClass expander "drag-over" h <- GTK.widgetGetHeight expander if y < 0.5 * fromIntegral h then do - GTK.widgetAddCssClass expander "drag-top" + widgetAddClass expander "drag-top" else do - GTK.widgetAddCssClass expander "drag-bot" + widgetAddClass expander "drag-bot" mbNextItem <- getNextItem_maybe expander for_ mbNextItem $ \ nextItem -> do - GTK.widgetAddCssClass nextItem "drag-top" + widgetAddClass nextItem "drag-top" return [ GDK.DragActionCopy ] void $ GTK.onDropTargetMotion dropTarget $ \ _x y -> do h <- GTK.widgetGetHeight expander if y < 0.5 * fromIntegral h then do GTK.widgetRemoveCssClass expander "drag-bot" - GTK.widgetAddCssClass expander "drag-top" + widgetAddClass expander "drag-top" mbNextItem <- getNextItem_maybe expander for_ mbNextItem $ \ nextItem -> do GTK.widgetRemoveCssClass nextItem "drag-top" else do GTK.widgetRemoveCssClass expander "drag-top" - GTK.widgetAddCssClass expander "drag-bot" + widgetAddClass expander "drag-bot" mbNextItem <- getNextItem_maybe expander for_ mbNextItem $ \ nextItem -> do - GTK.widgetAddCssClass nextItem "drag-top" + widgetAddClass nextItem "drag-top" return [ GDK.DragActionCopy ] void $ GTK.onDropTargetLeave dropTarget $ do dropTargetCleanup GTK.widgetAddController expander dragSource GTK.widgetAddController expander dropTarget + GTK.widgetAddController expander click -- Connect to "bind" signal to modify the generic widget to display the data for this list item. _ <- GTK.onSignalListItemFactoryBind layersListFactory $ \ listItem0 -> do