Skip to content

Commit

Permalink
Add support for an input modal 🚀 (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
kasper committed Feb 13, 2022
1 parent bdac643 commit 7f8ba59
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 10 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Release: dd.mm.yyyy
### New

- Improved documentation! 👩🏼‍💻 The documentation is now powered by Docusaurus and hosted at https://kasper.github.io/phoenix/.
- Add support for an input modal 🚀 ([#163](https://github.com/kasper/phoenix/issues/163)).
- Add support for device sleep events ([#282](https://github.com/kasper/phoenix/issues/282)).
- Add support for moving windows to spaces in macOS 12.0 with `Space#moveWindows(...)` ([#289](https://github.com/kasper/phoenix/issues/289)).

Expand Down Expand Up @@ -36,6 +37,11 @@ Release: dd.mm.yyyy
- New: Event `deviceWillSleep` is triggered when the device will sleep ([#282](https://github.com/kasper/phoenix/issues/282)).
- New: Event `deviceDidWake` is triggered when the device did wake ([#282](https://github.com/kasper/phoenix/issues/282)).

#### Modal

- New: Property `isInput` for setting whether the modal behaves as an input modal ([#163](https://github.com/kasper/phoenix/issues/163)).
- New: Property `textDidChange` for setting the callback function to call when the input modal’s text field value changes ([#163](https://github.com/kasper/phoenix/issues/163)).

#### Space

- New: Function `moveWindows(...)` moves the given windows to the space (macOS 10.13+) ([#289](https://github.com/kasper/phoenix/issues/289)).
Expand Down
25 changes: 17 additions & 8 deletions Phoenix/ModalWindow.xib
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
<outlet property="iconViewZeroWidthConstraint" destination="xs9-PA-Xeb" id="uGS-va-MjZ"/>
<outlet property="separatorConstraint" destination="tpi-5b-ira" id="bfF-oB-1yG"/>
<outlet property="textField" destination="Eu9-Hf-kgC" id="Mro-8U-aEF"/>
<outlet property="textFieldInputWidthConstraint" destination="Eq4-Dg-snr" id="53D-vv-zna"/>
<outlet property="textFieldTextWidthConstraint" destination="lWe-ze-4L4" id="sxi-Se-X2W"/>
<outlet property="window" destination="QvC-M9-y7g" id="VWl-Ud-pAe"/>
</connections>
</customObject>
Expand All @@ -21,14 +23,14 @@
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hasShadow="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="PHModalWindow">
<windowStyleMask key="styleMask" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="0.0" y="0.0" width="152" height="59"/>
<rect key="contentRect" x="0.0" y="0.0" width="148" height="58"/>
<rect key="screenRect" x="0.0" y="0.0" width="1512" height="944"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="152" height="58"/>
<rect key="frame" x="0.0" y="0.0" width="148" height="58"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="O5f-kD-6s1" userLabel="Container View">
<rect key="frame" x="0.0" y="0.0" width="152" height="58"/>
<rect key="frame" x="0.0" y="0.0" width="148" height="58"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="peX-kY-Jau" userLabel="Icon View">
<rect key="frame" x="17" y="5" width="47" height="47"/>
Expand All @@ -41,19 +43,26 @@
<binding destination="-2" name="value" keyPath="self.icon" id="Ixv-nc-huL"/>
</connections>
</imageView>
<textField horizontalHuggingPriority="750" verticalHuggingPriority="750" verticalCompressionResistancePriority="1000" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Eu9-Hf-kgC" userLabel="Text Field">
<rect key="frame" x="72" y="15" width="65" height="28"/>
<textField focusRingType="none" horizontalHuggingPriority="750" verticalHuggingPriority="750" verticalCompressionResistancePriority="1000" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Eu9-Hf-kgC" userLabel="Text Field">
<rect key="frame" x="72" y="15" width="61" height="28"/>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="800" id="lWe-ze-4L4"/>
<constraint firstAttribute="width" priority="250" constant="600" id="Eq4-Dg-snr"/>
<constraint firstAttribute="width" relation="lessThanOrEqual" priority="750" constant="800" id="lWe-ze-4L4"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Label" id="FT7-fN-TGb">
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" focusRingType="none" alignment="left" title="Label" id="FT7-fN-TGb">
<font key="font" metaFont="system" size="24"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.text" id="wWV-Bb-h0C"/>
<binding destination="-2" name="editable" keyPath="self.isInput" id="1T6-Bi-8Xd"/>
<binding destination="-2" name="value" keyPath="self.text" id="b1H-8L-8X6">
<dictionary key="options">
<bool key="NSContinuouslyUpdatesValue" value="YES"/>
</dictionary>
</binding>
<binding destination="-2" name="fontSize" keyPath="self.weight" id="cKK-hg-40t"/>
<outlet property="delegate" destination="-2" id="9es-G1-SKC"/>
</connections>
</textField>
</subviews>
Expand Down
7 changes: 7 additions & 0 deletions Phoenix/PHModalWindow.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,11 @@ - (instancetype) initWithContentRect:(NSRect)contentRect
return self;
}

#pragma mark - Key Status

- (BOOL) canBecomeKeyWindow {

return YES;
}

@end
4 changes: 4 additions & 0 deletions Phoenix/PHModalWindowController.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
@property (copy) NSString *appearance;
@property NSImage *icon;
@property (copy) NSString *text;
@property BOOL isInput;
@property JSValue *textDidChange;

// TODO: Deprecated and will be removed in later versions, use “text” instead
@property (copy) NSString *message;
Expand Down Expand Up @@ -47,6 +49,8 @@
@property (copy) NSString *appearance;
@property NSImage *icon;
@property (copy) NSString *text;
@property BOOL isInput;
@property JSValue *textDidChange;

// TODO: Deprecated and will be removed in later versions, use “text” instead
@property (copy) NSString *message;
Expand Down
25 changes: 24 additions & 1 deletion Phoenix/PHModalWindowController.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ @interface PHModalWindowController ()

@property (weak) IBOutlet NSLayoutConstraint *iconViewZeroWidthConstraint;
@property (weak) IBOutlet NSLayoutConstraint *separatorConstraint;
@property (weak) IBOutlet NSLayoutConstraint *textFieldTextWidthConstraint;
@property (weak) IBOutlet NSLayoutConstraint *textFieldInputWidthConstraint;

@end

Expand Down Expand Up @@ -82,6 +84,18 @@ - (NSString *) windowNibName {
return @"ModalWindow";
}

#pragma mark - NSControlTextEditingDelegate

- (void) controlTextDidChange:(NSNotification *)__unused notification {

JSValue *callback = self.textDidChange;
NSString *value = self.text ? self.text : @"";

if (!callback.isUndefined) {
[callback callWithArguments:@[ value ]];
}
}

#pragma mark - Appearance

- (PHModalWindowControllerAppearanceMaterial) material {
Expand Down Expand Up @@ -212,13 +226,15 @@ - (void) observeValueForKeyPath:(NSString *)keyPath

- (BOOL) isDisplayable {

return self.icon || [self hasText];
return self.icon || self.isInput || [self hasText];
}

- (void) layout {

self.iconViewZeroWidthConstraint.priority = !self.icon ? 999 : NSLayoutPriorityDefaultLow;
self.separatorConstraint.constant = (!self.icon || ![self hasText]) ? 0.0 : 10.0;
self.textFieldTextWidthConstraint.priority = self.isInput ? NSLayoutPriorityDefaultLow : NSLayoutPriorityDefaultHigh;
self.textFieldInputWidthConstraint.priority = self.isInput ? NSLayoutPriorityDefaultHigh : NSLayoutPriorityDefaultLow;
}

- (NSRect) frame {
Expand Down Expand Up @@ -249,6 +265,13 @@ - (void) show {
[self setupVibrantAppearance];
}

self.window.ignoresMouseEvents = !self.isInput;

if (self.isInput) {
// Required for text field to become key
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}

[self showWindow:self];
[self fadeWindowToAlpha:1.0 completionHandler:^{

Expand Down
23 changes: 22 additions & 1 deletion docs/docs/api/17-modal.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class Modal implements Identifiable
property String appearance
property Image icon
property String text
property boolean isInput
property Function textDidChange

constructor Modal Modal()
Rectangle frame()
Expand All @@ -39,6 +41,11 @@ end
- `icon` dynamic property for the icon displayed in the modal
- `text` dynamic property for the text displayed in the modal

### 2.7.0+

- `isInput` dynamic property for whether the modal behaves as an input modal
- `textDidChange` callback function to call when the input modal’s text field value changes, receives the value as the first argument for the callback

## Constructor

- `new Modal()` constructs and returns a new modal
Expand All @@ -59,5 +66,19 @@ Modal.build({
appearance: 'dark',
icon: App.get('Phoenix').icon(),
text: 'Hello World!',
}).show()
}).show();

// Show an input modal in the middle of the main screen
const screenFrame = Screen.main().flippedVisibleFrame();
const modal = new Modal();
modal.isInput = true;
modal.appearance = 'light';
modal.origin = {
x: screenFrame.width / 2 - modal.frame().width / 2,
y: screenFrame.height / 2 - modal.frame().height / 2,
};
modal.textDidChange = (value) => {
console.log('Text did change:', value);
};
modal.show();
```

0 comments on commit 7f8ba59

Please sign in to comment.