IDEA-194199: fixed layout (width with insets) for dialog buttons

This commit is contained in:
Artem Bochkarev
2018-06-19 19:57:18 +07:00
parent 593d7690ec
commit 04edc0122c
6 changed files with 162 additions and 54 deletions
Binary file not shown.
+104 -32
View File
@@ -74,6 +74,20 @@ const NSSize g_defaultMinSize = {72, 30}; // empiric value
//
// [NSGraphicsContext restoreGraphicsState];
//}
//- (void)drawDebugRect:(NSRect)frame color:(NSColor *)col {
// // NSLog(@"drawDebugRect: %@", NSStringFromRect(frame));
//
// [[NSGraphicsContext currentContext] saveGraphicsState];
//
// if (col == nil)
// col = [NSColor colorWithRed:105/255.0 green:211/255.0 blue:232/255.0 alpha:1.0];
// [col set];
// [col setStroke];
// [col setFill];
// NSFrameRect(frame);
//
// [NSGraphicsContext restoreGraphicsState];
//}
- (NSSize)cellSizeForBounds:(NSRect)rect {
if (self.title == nil) {
// NSLog(@"\t empty text, use default size %@", NSStringFromSize(g_defaultMinSize));
@@ -90,43 +104,56 @@ const NSSize g_defaultMinSize = {72, 30}; // empiric value
}
- (void)drawInteriorWithFrame:(NSRect)frame inView:(NSView *)controlView {
// NSLog(@"drawInteriorWithFrame: %@", NSStringFromRect(frame));
if (self.image == nil) {
[super drawTitle:self.attributedTitle withFrame:frame inView:controlView];
return;
}
if (self.title == nil) {
if (self.title == nil || self.title.length <= 0) {
[super drawImage:self.image withFrame:frame inView:controlView];
return;
}
const CGFloat imgW = self.image.size.width;
const CGFloat imgW = self.image == nil ? 0 : self.image.size.width;
const CGFloat imgWithMargin = imgW > 0 ? self.myMargin + imgW : 0;
NSSize txtSize = [self.title sizeWithAttributes:@{ NSFontAttributeName:self.font }];
const CGFloat txtW = txtSize.width;
const CGFloat fullW = self.myBorder*2 + txtW + self.myMargin + imgW;
const CGFloat fullW = self.myBorder*2 + txtW + imgWithMargin;
NSRect rcImg = frame;
rcImg.size.width = imgW;
NSRect rcTxt = frame;
if (fullW <= frame.size.width) {
const CGFloat delta = frame.size.width - fullW;
rcImg.origin.x = delta/2 + self.myBorder;
rcImg.size.width = imgW;
rcTxt.origin.x = rcImg.origin.x + imgW + self.myMargin;
rcTxt.size.width = txtW + self.myBorder;
if (imgW > 0) {
rcImg.origin.x = delta/2 + self.myBorder;
rcTxt.origin.x = rcImg.origin.x + imgWithMargin;
rcTxt.size.width = txtW + 1;
} else {
rcTxt.origin.x = delta/2 + self.myBorder;
rcTxt.size.width = txtW + 1;
}
} else {
rcImg.origin.x = self.myBorder;
rcImg.size.width = imgW;
rcTxt.origin.x = rcImg.origin.x + imgW + self.myMargin;
rcTxt.size.width = frame.size.width - self.myBorder - rcTxt.origin.x;
if (imgW > 0) {
rcImg.origin.x = self.myBorder;
rcTxt.origin.x = rcImg.origin.x + imgWithMargin;
rcTxt.size.width = frame.size.width - self.myBorder - rcTxt.origin.x;
} else {
rcTxt.origin.x = self.myBorder;
rcTxt.size.width = frame.size.width - self.myBorder*2;
}
}
if (rcImg.size.width > 0)
if (imgW > 0) {
[super drawImage:self.image withFrame:rcImg inView:controlView];
if (rcTxt.size.width > 0)
// NSColor * col = [NSColor colorWithRed:105/255.0 green:211/255.0 blue:0/255.0 alpha:1.0];
// [self drawDebugRect:rcImg color:col];
}
if (rcTxt.size.width > 0) {
[super drawTitle:self.attributedTitle withFrame:rcTxt inView:controlView];
// NSColor * col = [NSColor colorWithRed:105/255.0 green:0/255.0 blue:255/255.0 alpha:1.0];
// [self drawDebugRect:rcTxt color:col];
}
// [self drawDebugRect:frame color:nil];
}
@end
const int BUTTON_UPDATE_WIDTH = 1;
const int BUTTON_UPDATE_LAYOUT = 1;
const int BUTTON_UPDATE_FLAGS = 1 << 1;
const int BUTTON_UPDATE_TEXT = 1 << 2;
const int BUTTON_UPDATE_IMG = 1 << 3;
@@ -138,14 +165,35 @@ const int BUTTON_FLAG_SELECTED = 1 << 1;
const int BUTTON_FLAG_COLORED = 1 << 2;
const int BUTTON_FLAG_TOGGLE = 1 << 3;
const int BUTTON_PRIORITY_SHIFT = 3*8;
const unsigned int BUTTON_PRIORITY_MASK = 0xFF << BUTTON_PRIORITY_SHIFT;
const unsigned int LAYOUT_WIDTH_MASK = 0x0FFF;
const unsigned int LAYOUT_FLAG_MIN_WIDTH = 1 << 15;
const unsigned int LAYOUT_FLAG_MAX_WIDTH = 1 << 14;
const unsigned int LAYOUT_MARGIN_SHIFT = 2*8;
const unsigned int LAYOUT_MARGIN_MASK = 0xFF << LAYOUT_MARGIN_SHIFT;
const unsigned int LAYOUT_BORDER_SHIFT = 3*8;
const unsigned int LAYOUT_BORDER_MASK = 0xFF << LAYOUT_BORDER_SHIFT;
const int BUTTON_PRIORITY_SHIFT = 3*8;
const unsigned int BUTTON_PRIORITY_MASK = 0xFF << BUTTON_PRIORITY_SHIFT;
static int _getPriority(int flags) {
return (flags & BUTTON_PRIORITY_MASK) >> BUTTON_PRIORITY_SHIFT;
}
static void _setButtonData(NSButtonJAction *button, int updateOptions, int buttonWidth, int buttonFlags, NSString *nstext, NSImage *img, execute jaction) {
static int _getWidth(int layoutBits) {
return (layoutBits & LAYOUT_WIDTH_MASK);
}
static int _getMargin(int layoutBits) {
return (layoutBits & LAYOUT_MARGIN_MASK) >> LAYOUT_MARGIN_SHIFT;
}
static int _getBorder(int layoutBits) {
return (layoutBits & LAYOUT_BORDER_MASK) >> LAYOUT_BORDER_SHIFT;
}
static void _setButtonData(NSButtonJAction *button, int updateOptions, int layoutBits, int buttonFlags, NSString *nstext, NSImage *img, execute jaction) {
if (updateOptions & BUTTON_UPDATE_ACTION) {
button.jaction = jaction;
if (jaction) {
@@ -169,12 +217,36 @@ static void _setButtonData(NSButtonJAction *button, int updateOptions, int butto
if (updateOptions & BUTTON_UPDATE_IMG)
[button setImage:img];
if (updateOptions & BUTTON_UPDATE_WIDTH) {
button.bwidth = buttonWidth;
if (button.bwidth > 0.1f)
[button.widthAnchor constraintEqualToConstant:button.bwidth].active = YES;
else
if (updateOptions & BUTTON_UPDATE_LAYOUT) {
const int width = _getWidth(layoutBits);
button.bwidth = width;
if (width > 0) {
bool isMinWidth = (layoutBits & LAYOUT_FLAG_MIN_WIDTH) != 0;
bool isMaxWidth = (layoutBits & LAYOUT_FLAG_MAX_WIDTH) != 0;
if (isMinWidth && isMaxWidth) {
nserror(@"invalid arguments specified: both min and max bits are 1");
}
if (isMinWidth) {
//NSLog(@"set min width %d", width);
[button.widthAnchor constraintGreaterThanOrEqualToConstant:button.bwidth].active = YES;
} else if (isMaxWidth) {
//NSLog(@"set max width %d", width);
[button.widthAnchor constraintLessThanOrEqualToConstant:button.bwidth].active = YES;
} else {
//NSLog(@"set const width %d", width);
[button.widthAnchor constraintEqualToConstant:button.bwidth].active = YES;
}
} else
[button.widthAnchor constraintEqualToAnchor:button.widthAnchor].active = NO;
{ // process margins
const int margin = _getMargin(layoutBits);
const int border = _getBorder(layoutBits);
if (margin > 0 || border > 0) {
[button setMargins:margin border:border];
//NSLog(@"set insets: m=%d b=%d", margin, border);
}
}
}
if (updateOptions & BUTTON_UPDATE_FLAGS) {
@@ -224,7 +296,7 @@ static void _setButtonData(NSButtonJAction *button, int updateOptions, int butto
// NOTE: called from AppKit-thread (creation when TB becomes visible), uses default autorelease-pool (create before event processing)
id createButton(
const char *uid,
int buttonWidth,
int layoutBits,
int buttonFlags,
const char *text,
const char *raster4ByteRGBA, int w, int h,
@@ -238,7 +310,7 @@ id createButton(
NSImage *img = createImgFrom4ByteRGBA((const unsigned char *) raster4ByteRGBA, w, h);
NSString *nstext = createStringFromUTF8(text);
_setButtonData(button, BUTTON_UPDATE_ALL, buttonWidth, buttonFlags, nstext, img, jaction);
_setButtonData(button, BUTTON_UPDATE_ALL, layoutBits, buttonFlags, nstext, img, jaction);
customItemForButton.view = button; // NOTE: view is strong
const int prio = _getPriority(buttonFlags);
@@ -254,7 +326,7 @@ id createButton(
void updateButton(
id buttObj,
int updateOptions,
int buttonWidth,
int layoutBits,
int buttonFlags,
const char *text,
const char *raster4ByteRGBA, int w, int h,
@@ -269,13 +341,13 @@ void updateButton(
if ([NSThread isMainThread]) {
nstrace(@"sync update button [%@] (main thread: %@)", container.identifier, [NSThread currentThread]);
_setButtonData(button, updateOptions, buttonWidth, buttonFlags, nstext, img, jaction);
_setButtonData(button, updateOptions, layoutBits, buttonFlags, nstext, img, jaction);
} else {
nstrace(@"async update button [%@] (thread: %@)", container.identifier, [NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
// NOTE: block is copied, img/text objects is automatically retained
// nstrace(@"\tperform update button [%@] (thread: %@)", container.identifier, [NSThread currentThread]);
_setButtonData(button, updateOptions, buttonWidth, buttonFlags, nstext, img, jaction);
_setButtonData(button, updateOptions, layoutBits, buttonFlags, nstext, img, jaction);
});
}
@@ -26,6 +26,7 @@ import com.intellij.openapi.actionSystem.ex.QuickList;
import com.intellij.openapi.actionSystem.ex.QuickListsManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.KeyboardSettingsExternalizable;
import com.intellij.openapi.keymap.Keymap;
@@ -228,7 +229,8 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
});
panel.add(myShowFnCheckbox, BorderLayout.SOUTH);
}
} else
Logger.getInstance(KeymapPanel.class).error("can't obtain application id from NSBundle");
}
return panel;
@@ -44,6 +44,9 @@ class BuildUtils {
private static final String ourFlexibleSeparatorText = "type.flexible";
private static final int ourRunConfigurationPopoverWidth = 143;
private static final int BUTTON_MIN_WIDTH_DLG = 107;
private static final int BUTTON_BORDER = 16;
private static final int BUTTON_IMAGE_MARGIN = 2;
private static final String RUNNERS_GROUP_TOUCHBAR = "RunnerActionsTouchbar";
@@ -176,12 +179,14 @@ class BuildUtils {
defIndex = c;
continue;
}
group.addButton().setText(DialogWrapper.extractMnemonic(sb).second).setThreadSafeAction(actions[c]);
final TBItemButton tbb = group.addButton().setText(DialogWrapper.extractMnemonic(sb).second).setThreadSafeAction(actions[c]);
_setDialogLayout(tbb);
}
if (defIndex >= 0)
group.addButton().setText(DialogWrapper.extractMnemonic(buttons[defIndex]).second).setThreadSafeAction(actions[defIndex])
.setColored(true);
if (defIndex >= 0) {
final TBItemButton tbb = group.addButton().setText(DialogWrapper.extractMnemonic(buttons[defIndex]).second).setThreadSafeAction(actions[defIndex]).setColored(true);
_setDialogLayout(tbb);
}
result.setPrincipal(gr);
result.selectVisibleItemsToShow();
@@ -207,7 +212,8 @@ class BuildUtils {
continue;
// NOTE: must set different priorities for items, otherwise system can hide all items with the same priority (but some of them is able to be placed)
result.addAnActionButton(anAct, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(ob).setPriority(--prio);
final TBItemButton tbb = result.addAnActionButton(anAct, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(ob).setPriority(--prio);
_setDialogLayout(tbb);
}
}
}
@@ -226,13 +232,16 @@ class BuildUtils {
final AnAction anAct = _createAnAction(jb.getAction(), jb, false);
if (anAct == null)
continue;
group.addAnActionButton(anAct, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(jb);
final TBItemButton tbb = group.addAnActionButton(anAct, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(jb);
_setDialogLayout(tbb);
}
if (jbdef != null) {
final AnAction anAct = _createAnAction(jbdef.getAction(), jbdef, false);
if (anAct != null)
group.addAnActionButton(anAct, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(jbdef).setColored(true);
if (anAct != null) {
final TBItemButton tbb = group.addAnActionButton(anAct, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(jbdef).setColored(true);
_setDialogLayout(tbb);
}
}
result.selectVisibleItemsToShow();
@@ -283,6 +292,12 @@ class BuildUtils {
return tb;
}
private static void _setDialogLayout(TBItemButton button) {
if (button == null)
return;
button.setLayout(BUTTON_MIN_WIDTH_DLG, NSTLibrary.LAYOUT_FLAG_MIN_WIDTH, BUTTON_IMAGE_MARGIN, BUTTON_BORDER);
}
interface INodeFilter {
boolean skip(String nodeId);
}
@@ -47,7 +47,7 @@ public interface NSTLibrary extends Library {
ID createScrubber(String uid, int itemWidth, ScrubberItemData[] items, int count);
ID createGroupItem(String uid, ID[] items, int count);
int BUTTON_UPDATE_WIDTH = 1;
int BUTTON_UPDATE_LAYOUT = 1;
int BUTTON_UPDATE_FLAGS = 1 << 1;
int BUTTON_UPDATE_TEXT = 1 << 2;
int BUTTON_UPDATE_IMG = 1 << 3;
@@ -59,6 +59,14 @@ public interface NSTLibrary extends Library {
int BUTTON_FLAG_COLORED = 1 << 2;
int BUTTON_FLAG_TOGGLE = 1 << 3;
int LAYOUT_WIDTH_MASK = 0x0FFF;
int LAYOUT_FLAG_MIN_WIDTH = 1 << 15;
int LAYOUT_FLAG_MAX_WIDTH = 1 << 14;
int LAYOUT_MARGIN_SHIFT = 2*8;
int LAYOUT_MARGIN_MASK = 0xFF << LAYOUT_MARGIN_SHIFT;
int LAYOUT_BORDER_SHIFT = 3*8;
int LAYOUT_BORDER_MASK = 0xFF << LAYOUT_BORDER_SHIFT;
int BUTTON_PRIORITY_SHIFT = 3*8;
int BUTTON_PRIORITY_MASK = 0xFF << BUTTON_PRIORITY_SHIFT;
@@ -69,4 +77,6 @@ public interface NSTLibrary extends Library {
void updateScrubber(ID scrubObj, int itemWidth, ScrubberItemData[] items, int count);
static int priority2mask(byte prio) { return (prio + 128) << BUTTON_PRIORITY_SHIFT; }
static int margin2mask(byte margin) { return ((int)margin & 0xFF) << LAYOUT_MARGIN_SHIFT; }
static int border2mask(byte border) { return ((int)border & 0xFF) << LAYOUT_BORDER_SHIFT; }
}
@@ -14,7 +14,7 @@ import javax.swing.*;
class TBItemButton extends TBItem {
protected @Nullable Icon myIcon;
protected @Nullable String myText;
protected int myWidth = -1;
protected int myLayoutBits = 0;
protected int myFlags = 0;
private @Nullable Runnable myAction;
@@ -83,12 +83,24 @@ class TBItemButton extends TBItem {
return this;
}
TBItemButton setWidth(int width) { return setLayout(width, 0, 2, 8); }
TBItemButton setWidth(int width) {
if (width != myWidth) {
myWidth = width;
TBItemButton setLayout(int width, int widthFlags, int margin, int border) {
if (width < 0)
width = 0;
if (margin < 0)
margin = 0;
if (border < 0)
border = 0;
int newLayout = width & NSTLibrary.LAYOUT_WIDTH_MASK;
newLayout |= widthFlags;
newLayout |= NSTLibrary.margin2mask((byte)margin);
newLayout |= NSTLibrary.border2mask((byte)border);
if (myLayoutBits != newLayout) {
myLayoutBits = newLayout;
if (myNativePeer != ID.NIL) {
myUpdateOptions |= NSTLibrary.BUTTON_UPDATE_WIDTH;
myUpdateOptions |= NSTLibrary.BUTTON_UPDATE_LAYOUT;
_updateNativePeer();
}
}
@@ -141,7 +153,7 @@ class TBItemButton extends TBItem {
int flags = _applyFlag(myFlags, isSelected, NSTLibrary.BUTTON_FLAG_SELECTED);
flags = _applyFlag(flags, isDisabled, NSTLibrary.BUTTON_FLAG_DISABLED);
_update(icon, text, myAction, myWidth, flags);
_update(icon, text, myAction, flags);
}
private static boolean _equals(Icon ic0, Icon ic1) {
@@ -150,7 +162,7 @@ class TBItemButton extends TBItem {
return ic0 != null ? ic0.equals(ic1) : ic1.equals(ic0);
}
synchronized private void _update(Icon icon, String text, Runnable action, int buttWidth, int buttFlags) {
synchronized private void _update(Icon icon, String text, Runnable action, int buttFlags) {
if (myNativePeer != ID.NIL) {
if (!_equals(icon, myIcon)) {
// NOTE: some of layered buttons (like 'stop' or 'debug') can change the icon-object permanently (every second) without any visible differences
@@ -161,15 +173,12 @@ class TBItemButton extends TBItem {
myUpdateOptions |= NSTLibrary.BUTTON_UPDATE_TEXT;
if (action != myAction)
myUpdateOptions |= NSTLibrary.BUTTON_UPDATE_ACTION;
if (buttWidth != myWidth)
myUpdateOptions |= NSTLibrary.BUTTON_UPDATE_WIDTH;
if (buttFlags != myFlags)
myUpdateOptions |= NSTLibrary.BUTTON_UPDATE_FLAGS;
}
myIcon = icon;
myText = text;
myAction = action;
myWidth = buttWidth;
myFlags = buttFlags;
if (myUpdateOptions != 0)
updateNativePeer();
@@ -182,14 +191,14 @@ class TBItemButton extends TBItem {
final NSTLibrary.Action callback = (myUpdateOptions & NSTLibrary.BUTTON_UPDATE_ACTION) != 0 ? myNativeCallback : null;
// System.out.printf("_updateNativePeer, button [%s]: updateOptions 0x%X\n", myUid, myUpdateOptions);
final int validFlags = _validateFlags();
NST.updateButton(myNativePeer, myUpdateOptions, myWidth, validFlags, text, icon, callback);
NST.updateButton(myNativePeer, myUpdateOptions, myLayoutBits, validFlags, text, icon, callback);
myUpdateOptions = 0;
}
@Override
synchronized protected ID _createNativePeer() {
// System.out.printf("_createNativePeer, button [%s]\n", myUid);
return NST.createButton(myUid, myWidth, _validateFlags(), myText, myIcon, myNativeCallback);
return NST.createButton(myUid, myLayoutBits, _validateFlags(), myText, myIcon, myNativeCallback);
}
private int _validateFlags() {