Patch for nudge keys


#1

I wanted nudge keys in the subcomponent layout editor to prevent wrist cramping, so I hacked Jucer to add them. A patch, made w/ diff -Naur, is at the end of this post.

I have tried to keep to Mr Storer’s coding conventions as I understand them, but only he knows whether the hack was done well or not. (Anyway I think I learned a good deal about JUCE so it wasn’t a waste for me.)

Usage when only one element is selected:

  • Arrow keys move selected element by one pixel
  • Arrow keys + alt move selected element according to the snap grid
  • Up / down + shift change the height by moving the bottom edge by one pixel
  • Left / right + shift change the width by moving the right edge by one pixel
  • Up / down + shift + alt change the height by moving the bottom edge to the snap grid
  • Left / right + shift + alt change the width by moving the right edge to the snap grid

Usage when many elements are selected:

  • Arrow keys move selected element by one pixel
  • Arrow keys + alt move selected element by n pixels where n is the snap resolution
  • Up / down + shift change the height by moving the bottom edge by one pixel
  • Left / right + shift change the width by moving the right edge by one pixel
  • Up / down + shift + alt change the height by n pixels where n is the snap resolution
  • Left / right + shift + alt change the width by n pixels where n is the snap resolution

The difference is only in snapping. When many elements are selected no attempt is made to snap one of them to the grid; instead the snap size is used as an increment only. This is similar to how dragging works.

Snapping is a bit inelegant when the component bounds are not on the grid – the dimension may jump over a snap line. I don’t care about this enough to fix it, but I did ensure that the snap won’t go backwards. (Of course this only applies to single selections)

Here is the patch:

diff -Naur -x '*~' c:/juce/jucer/src/model/jucer_ComponentLayout.cpp c:/juce/jucer-mpa/src/model/jucer_ComponentLayout.cpp
--- c:/juce/jucer/src/model/jucer_ComponentLayout.cpp	2006-06-07 11:57:00.000000000 -0700
+++ c:/juce/jucer-mpa/src/model/jucer_ComponentLayout.cpp	2006-07-06 20:42:12.000000000 -0700
@@ -504,9 +504,9 @@
     document->getUndoManager().beginNewTransaction();
 }
 
-void ComponentLayout::dragSelectedComps (int dx, int dy)
+void ComponentLayout::dragSelectedComps (int dx, int dy, bool allowSnap)
 {
-    if (document != 0 && selected.getNumSelected() > 1)
+    if (allowSnap && (document != 0 && selected.getNumSelected() > 1))
     {
         dx = document->snapPosition (dx);
         dy = document->snapPosition (dy);
@@ -521,8 +521,8 @@
 
         if (document != 0 && selected.getNumSelected() == 1)
         {
-            c->setTopLeftPosition (document->snapPosition (startX + dx),
-                                   document->snapPosition (startY + dy));
+            c->setTopLeftPosition (allowSnap ? document->snapPosition (startX + dx) : startX + dx,
+                                   allowSnap ? document->snapPosition (startY + dy) : startY + dy);
         }
         else
         {
@@ -560,6 +560,48 @@
     document->getUndoManager().beginNewTransaction();
 }
 
+void ComponentLayout::moveSelectedComps (int dx, int dy, bool snap)
+{
+    startDragging();
+    dragSelectedComps (dx, dy, snap);
+    endDragging();
+}
+
+void ComponentLayout::stretchSelectedComps (int dw, int dh, bool allowSnap)
+{
+    int neww,newh;
+    if (document != 0 && selected.getNumSelected() == 1)
+    {
+        Component *const c = selected.getSelectedItem(0);
+        int oldw = c->getWidth();
+        int oldh = c->getHeight();
+        if (allowSnap) {
+            int bot = c->getY() + oldh + dh;
+            int right = c->getX() + oldw + dw;
+            bot = dh ? document->snapPosition (bot) : bot;
+            right = dw ? document->snapPosition (right) : right;
+            newh = bot - c->getY();
+            neww = right - c->getX();
+        } else {
+            newh = oldh + dh;
+            neww = oldw + dw;
+        }
+        c->setSize (neww, newh);
+
+        updateStoredComponentPosition (c, true);
+    } 
+    else for (int i = 0; i < selected.getNumSelected(); ++i)
+    {
+        Component* const c = selected.getSelectedItem (i);
+
+        neww = c->getWidth() + dw;
+        newh = c->getHeight() + dh;
+        c->setSize (neww,newh);
+
+        updateStoredComponentPosition (c, true);
+    }
+}
+
 //==============================================================================
 void ComponentLayout::fillInGeneratedCode (GeneratedCode& code) const
 {
diff -Naur -x '*~' c:/juce/jucer/src/model/jucer_ComponentLayout.h c:/juce/jucer-mpa/src/model/jucer_ComponentLayout.h
--- c:/juce/jucer/src/model/jucer_ComponentLayout.h	2006-06-07 11:57:00.000000000 -0700
+++ c:/juce/jucer-mpa/src/model/jucer_ComponentLayout.h	2006-07-06 19:36:40.000000000 -0700
@@ -86,9 +86,12 @@
     void selectedToBack();
 
     void startDragging();
-    void dragSelectedComps (int dxFromDragStart, int dyFromDragStart);
+    void dragSelectedComps (int dxFromDragStart, int dyFromDragStart, bool allowSnap = true);
     void endDragging();
 
+    void moveSelectedComps (int dx, int dy, bool snap);
+    void stretchSelectedComps (int dw, int dh, bool allowSnap);
+
     void bringLostItemsBackOnScreen (int width, int height);
 
     //==============================================================================
diff -Naur -x '*~' c:/juce/jucer/src/ui/jucer_ComponentLayoutEditor.cpp c:/juce/jucer-mpa/src/ui/jucer_ComponentLayoutEditor.cpp
--- c:/juce/jucer/src/ui/jucer_ComponentLayoutEditor.cpp	2006-06-07 11:57:00.000000000 -0700
+++ c:/juce/jucer-mpa/src/ui/jucer_ComponentLayoutEditor.cpp	2006-07-06 20:31:00.000000000 -0700
@@ -282,6 +282,50 @@
         layout.getSelectedSet().deselectAll();
 }
 
+static void moveOrStretch (ComponentLayout& layout, int x, int y, bool snap, bool stretch)
+{
+    if (stretch)
+        layout.stretchSelectedComps (x, y, snap);
+    else
+        layout.moveSelectedComps (x, y, snap);
+}
+
+void ComponentLayoutEditor::keyPressed (const KeyPress& key)
+{
+    int kc = key.getKeyCode();
+    int amt;
+    bool snap,stretch;
+
+    snap = key.getModifiers().isAltDown();
+    stretch = key.getModifiers().isShiftDown();
+
+    if (snap)
+        amt = document.getSnappingGridSize()+1;
+    else
+        amt = 1;
+
+    if (kc == KeyPress::rightKey)
+    {
+        moveOrStretch (layout, amt, 0, snap, stretch);
+    }
+    else if (kc == KeyPress::downKey)
+    {
+        moveOrStretch (layout, 0,amt, snap, stretch);
+    }
+    else if (kc == KeyPress::leftKey)
+    {
+        moveOrStretch (layout, -amt, 0, snap, stretch);
+    }
+    else if (kc == KeyPress::upKey)
+    {
+        moveOrStretch (layout, 0, -amt, snap, stretch);
+    }
+    else
+    {
+        Component::keyPressed (key);
+    }
+}
+
 bool ComponentLayoutEditor::filesDropped (const StringArray& filenames, int x, int y)
 {
     File f (filenames [0]);
diff -Naur -x '*~' c:/juce/jucer/src/ui/jucer_ComponentLayoutEditor.h c:/juce/jucer-mpa/src/ui/jucer_ComponentLayoutEditor.h
--- c:/juce/jucer/src/ui/jucer_ComponentLayoutEditor.h	2006-06-07 11:57:00.000000000 -0700
+++ c:/juce/jucer-mpa/src/ui/jucer_ComponentLayoutEditor.h	2006-07-06 20:30:54.000000000 -0700
@@ -73,6 +73,8 @@
 
     const Rectangle getComponentArea() const;
 
+    void keyPressed (const KeyPress& key);
+
     //==============================================================================
     juce_UseDebuggingNewOperator
 

[/code][/list]


#2

Cool - I can add that to the build if you like? (it’ll be just in time for the next release, which I’m going to try to do today).


#3

Absolutely you can include it – it’s all yours if you like it! best --mpa


#4

Already did!
(TBH it should have been done using application command shortcuts rather than intercepting keyPressed() directly, but I couldn’t be bothered doing all the extra typing…)


#5

Ah, you’re right … well, umm … I like your excuse :slight_smile:

Personally I would never invoke these commands from menus or buttons etc. but certainly app commands are the right way to do things here. Next time then …


#6