[Datalist] Add button to TextFieldInputs with a datalist
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeMac.mm
index 37665fd..5faf50f 100644 (file)
@@ -197,6 +197,74 @@ static const double progressAnimationNumFrames = 256;
 @implementation WebCoreRenderThemeBundle
 @end
 
+#if ENABLE(DATALIST_ELEMENT)
+
+static const CGFloat listButtonWidth = 16.0f;
+static const CGFloat listButtonCornerRadius = 5.0f;
+
+@interface WebListButtonCell : NSCell
+@end
+
+@implementation WebListButtonCell
+- (void)drawWithFrame:(NSRect)cellFrame inView:(__unused NSView *)controlView
+{
+    CGFloat listButtonCornerRadius = 5.0f;
+    NSPoint topLeft = NSMakePoint(NSMinX(cellFrame), NSMinY(cellFrame));
+    NSPoint topRight = NSMakePoint(NSMaxX(cellFrame), NSMinY(cellFrame));
+    NSPoint bottomRight = NSMakePoint(NSMaxX(cellFrame), NSMaxY(cellFrame));
+    NSPoint bottomLeft = NSMakePoint(NSMinX(cellFrame), NSMaxY(cellFrame));
+
+    NSBezierPath *path = [NSBezierPath bezierPath];
+    [path moveToPoint:topLeft];
+
+    [path lineToPoint:NSMakePoint(topRight.x - listButtonCornerRadius, topRight.y)];
+    [path curveToPoint:NSMakePoint(topRight.x, topRight.y + listButtonCornerRadius) controlPoint1:topRight controlPoint2:topRight];
+
+    [path lineToPoint:NSMakePoint(bottomRight.x, bottomRight.y - listButtonCornerRadius)];
+    [path curveToPoint:NSMakePoint(bottomRight.x - listButtonCornerRadius, bottomRight.y) controlPoint1:bottomRight controlPoint2:bottomRight];
+
+    [path lineToPoint:bottomLeft];
+    [path lineToPoint:topLeft];
+
+    if ([self userInterfaceLayoutDirection] == NSUserInterfaceLayoutDirectionRightToLeft) {
+        NSAffineTransform *transform = [NSAffineTransform transform];
+        [transform translateXBy:NSMidX(cellFrame) yBy:NSMidY(cellFrame)];
+        [transform rotateByDegrees:180];
+        [transform translateXBy:-1 * NSMidX(cellFrame) yBy:-1 * NSMidY(cellFrame)];
+        [path transformUsingAffineTransform:transform];
+    }
+
+    // FIXME: Obtain the gradient colors from CoreUI or AppKit
+    RetainPtr<NSGradient> gradient;
+    NSUserAccentColor accentColor = NSColorGetUserAccentColor();
+    if (accentColor == NSUserAccentColorRed)
+        gradient = adoptNS([[NSGradient alloc] initWithStartingColor:[NSColor colorWithRed:(212.0 / 255) green:(122.0 / 255) blue:(117.0 / 255) alpha:1.0] endingColor:[NSColor colorWithRed:(189.0 / 255) green:(34.0 / 255) blue:(23.0 / 255) alpha:1.0]]);
+    else if (accentColor == NSUserAccentColorOrange)
+        gradient = adoptNS([[NSGradient alloc] initWithStartingColor:[NSColor colorWithRed:(242.0 / 255) green:(185.0 / 255) blue:(113.0 / 255) alpha:1.0] endingColor:[NSColor colorWithRed:(242.0 / 255) green:(145.0 / 255) blue:(17.0 / 255) alpha:1.0]]);
+    else if (accentColor == NSUserAccentColorYellow)
+        gradient = adoptNS([[NSGradient alloc] initWithStartingColor:[NSColor colorWithRed:(241.0 / 255) green:(212.0 / 255) blue:(119.0 / 255) alpha:1.0] endingColor:[NSColor colorWithRed:(239.0 / 255) green:(193.0 / 255) blue:(27.0 / 255) alpha:1.0]]);
+    else if (accentColor == NSUserAccentColorGreen)
+        gradient = adoptNS([[NSGradient alloc] initWithStartingColor:[NSColor colorWithRed:(132.0 / 255) green:(186.0 / 255) blue:(120.0 / 255) alpha:1.0] endingColor:[NSColor colorWithRed:(46.0 / 255) green:(145.0 / 255) blue:(30.0 / 255) alpha:1.0]]);
+    else if (accentColor == NSUserAccentColorPurple)
+        gradient = adoptNS([[NSGradient alloc] initWithStartingColor:[NSColor colorWithRed:(178.0 / 255) green:(128.0 / 255) blue:(175.0 / 255) alpha:1.0] endingColor:[NSColor colorWithRed:(130.0 / 255) green:(43.0 / 255) blue:(123.0 / 255) alpha:1.0]]);
+    else if (accentColor == NSUserAccentColorPink)
+        gradient = adoptNS([[NSGradient alloc] initWithStartingColor:[NSColor colorWithRed:(225.0 / 255) green:(126.0 / 255) blue:(165.0 / 255) alpha:1.0] endingColor:[NSColor colorWithRed:(211.0 / 255) green:(42.0 / 255) blue:(105.0 / 255) alpha:1.0]]);
+    else if (accentColor == NSUserAccentColorNoColor)
+        gradient = adoptNS([[NSGradient alloc] initWithStartingColor:[NSColor colorWithRed:(177.0 / 255) green:(177.0 / 255) blue:(182.0 / 255) alpha:1.0] endingColor:[NSColor colorWithRed:(145.0 / 255) green:(145.0 / 255) blue:(150.0 / 255) alpha:1.0]]);
+    else
+        gradient = adoptNS([[NSGradient alloc] initWithStartingColor:[NSColor colorWithRed:(114.0 / 255) green:(164.0 / 255) blue:(243.0 / 255) alpha:1.0] endingColor:[NSColor colorWithRed:(45.0 / 255) green:(117.0 / 255) blue:(246.0 / 255) alpha:1.0]]);
+
+    [gradient drawInBezierPath:path angle:90];
+    if ([self isHighlighted]) {
+        NSColor *overlay = [NSColor colorWithWhite:0 alpha:0.1];
+        [overlay setFill];
+        [path fill];
+    }
+}
+@end
+
+#endif // ENABLE(DATALIST_ELEMENT)
+
 namespace WebCore {
 
 using namespace HTMLNames;
@@ -1005,6 +1073,54 @@ NSControlSize RenderThemeMac::controlSizeForSystemFont(const RenderStyle& style)
     return NSControlSizeMini;
 }
 
+#if ENABLE(DATALIST_ELEMENT)
+
+void RenderThemeMac::paintListButtonForInput(const RenderObject& o, GraphicsContext& context, const FloatRect& r)
+{
+    // We can't paint an NSComboBoxCell since they are not height-resizable.
+    const auto& input = downcast<HTMLInputElement>(*(o.generatingNode()));
+    NSCell *listButton = this->listButton();
+
+    NSRect listButtonFrame = NSMakeRect(r.maxX() - listButtonWidth, r.y(), listButtonWidth, r.height());
+    if (!o.style().isLeftToRightDirection()) {
+        listButtonFrame.origin.x = r.x();
+        [listButton setUserInterfaceLayoutDirection:NSUserInterfaceLayoutDirectionRightToLeft];
+    } else
+        [listButton setUserInterfaceLayoutDirection:NSUserInterfaceLayoutDirectionLeftToRight];
+
+    [listButton setHighlighted:input.isShowingList()];
+    if (!input.isShowingList())
+        updatePressedState(listButton, *(input.dataListButtonElement()->renderer()));
+
+    [listButton drawWithFrame:listButtonFrame inView:documentViewFor(o)];
+    [listButton setControlView:nil];
+
+    RefPtr<Image> image;
+    float imageScale = 1;
+    if (o.document().deviceScaleFactor() >= 2) {
+        image = Image::loadPlatformResource("ListButtonArrow@2x");
+        imageScale = 2;
+    } else
+        image = Image::loadPlatformResource("ListButtonArrow");
+
+    FloatRect imageRect(0, 0, image->width() / imageScale, image->height() / imageScale);
+    imageRect.setX(NSMidX(listButtonFrame) - imageRect.width() / 2);
+    imageRect.setY(NSMidY(listButtonFrame) - imageRect.height() / 2);
+
+    context.drawImage(*image, imageRect);
+}
+
+void RenderThemeMac::adjustListButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
+{
+    // Add a margin to place the button at end of the input field.
+    if (style.isLeftToRightDirection())
+        style.setMarginRight(Length(-4, Fixed));
+    else
+        style.setMarginLeft(Length(-4, Fixed));
+}
+
+#endif
+
 bool RenderThemeMac::paintTextField(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
 {
     LocalCurrentGraphicsContext localContext(paintInfo.context());
@@ -1031,6 +1147,15 @@ bool RenderThemeMac::paintTextField(const RenderObject& o, const PaintInfo& pain
 
     [textField setControlView:nil];
 
+#if ENABLE(DATALIST_ELEMENT)
+    if (!is<HTMLInputElement>(o.generatingNode()))
+        return false;
+
+    const auto& input = downcast<HTMLInputElement>(*(o.generatingNode()));
+    if (input.list())
+        paintListButtonForInput(o, paintInfo.context(), adjustedPaintRect);
+#endif
+
     return false;
 }
 
@@ -1788,6 +1913,15 @@ bool RenderThemeMac::paintSearchField(const RenderObject& o, const PaintInfo& pa
     [search setControlView:nil];
     [search resetSearchButtonCell];
 
+#if ENABLE(DATALIST_ELEMENT)
+    if (!is<HTMLInputElement>(o.generatingNode()))
+        return false;
+
+    const auto& input = downcast<HTMLInputElement>(*(o.generatingNode()));
+    if (input.list())
+        paintListButtonForInput(o, paintInfo.context(), FloatRect(unzoomedRect.x(), unzoomedRect.y() + 1, unzoomedRect.width(), unzoomedRect.height() - 2));
+#endif
+
     return false;
 }
 
@@ -1890,13 +2024,21 @@ bool RenderThemeMac::paintSearchFieldCancelButton(const RenderBox& box, const Pa
     float zoomLevel = box.style().effectiveZoom();
 
     FloatRect localBounds = adjustedCancelButtonRect([search cancelButtonRectForBounds:NSRect(snappedIntRect(inputBox.contentBoxRect()))]);
+
     // Adjust position based on the content direction.
     float adjustedXPosition;
-    if (box.style().direction() == RTL)
-        adjustedXPosition = inputBox.contentBoxRect().x();
-    else
-        adjustedXPosition = inputBox.contentBoxRect().maxX() - localBounds.size().width();
-    
+
+    if (is<HTMLInputElement>(*input)) {
+        RenderBox* cancelButtonBox = downcast<RenderBox>(downcast<HTMLInputElement>(*input).cancelButtonElement()->renderer());
+        // The cancel button won't always be the rightmost element
+        adjustedXPosition = inputBox.contentBoxRect().x() + (cancelButtonBox->absoluteContentBox().x() - inputBox.absoluteContentBox().x());
+    } else {
+        if (box.style().direction() == RTL)
+            adjustedXPosition = inputBox.contentBoxRect().x();
+        else
+            adjustedXPosition = inputBox.contentBoxRect().maxX() - localBounds.size().width();
+    }
+
     localBounds.setX(adjustedXPosition);
     FloatPoint paintingPos = convertToPaintingPosition(inputBox, box, localBounds.location(), r.location());
 
@@ -2227,6 +2369,16 @@ NSTextFieldCell* RenderThemeMac::textField() const
     return m_textField.get();
 }
 
+#if ENABLE(DATALIST_ELEMENT)
+NSCell *RenderThemeMac::listButton() const
+{
+    if (!m_listButton)
+        m_listButton = adoptNS([[WebListButtonCell alloc] init]);
+
+    return m_listButton.get();
+}
+#endif
+
 String RenderThemeMac::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
 {
     if (width <= 0)