How to change color of NSSearchField?

Mac OSX specific forum
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

How to change color of NSSearchField?

Post by Wolfram »

How can I change the background color of a NSSearchField?

Code: Select all

Global Window_0


Procedure OpenWindow_0(x = 0, y = 0, width = 450, height = 160)
  Window_0 = OpenWindow(#PB_Any, x, y, width, height, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  Frame.NSRect
  Frame\origin\x = 164
  Frame\origin\y = 10
  Frame\size\width = 120
  Frame\size\height = 25
  
  CocoaMessage(@SearchField, #Null, "NSSearchField alloc")
  
  CocoaMessage(0, SearchField, "initWithFrame:@", @Frame)
  
  CocoaMessage(@contentView, WindowID(Window_0), "contentView")
  
  CocoaMessage(0, contentView, "addSubview:", SearchField)
  
  CocoaMessage(0, SearchField, "cell")

  
EndProcedure

Procedure Window_0_Events(event)
  Select event
    Case #PB_Event_CloseWindow
      ProcedureReturn #False

    Case #PB_Event_Menu
      Select EventMenu()
	Case #PB_Menu_Quit
          ProcedureReturn #False
      EndSelect

    Case #PB_Event_Gadget
      Select EventGadget()
          
      EndSelect
  EndSelect
  ProcedureReturn #True
EndProcedure

OpenWindow_0()

Repeat
  event = WaitWindowEvent()
Until Window_0_Events(event) = #False

End
macOS Catalina 10.15.7
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: How to change color of NSSearchField?

Post by wilbert »

I doesn't seem to be easy.

This does something on my computer but only when I click on the search field.

Code: Select all

  Color = CocoaMessage(0, 0, "NSColor yellowColor")
  CocoaMessage(0, SearchField, "setBezelStyle:", 0)
  CocoaMessage(0, SearchField, "setBackgroundColor:", Color)
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: How to change color of NSSearchField?

Post by Wolfram »

wilbert wrote:I doesn't seem to be easy.

This does something on my computer but only when I click on the search field.

Code: Select all

  Color = CocoaMessage(0, 0, "NSColor yellowColor")
  CocoaMessage(0, SearchField, "setBezelStyle:", 0)
  CocoaMessage(0, SearchField, "setBackgroundColor:", Color)
Thanks Wilbert!
Here it is directly changed, but now it isn't round anymore.

I fond some information but I'm not able to translate it into PB.
https://www.64k-tec.de/2010/06/changing ... -controls/

Maybe it helps you.
macOS Catalina 10.15.7
User avatar
Shardik
Addict
Addict
Posts: 1988
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: How to change color of NSSearchField?

Post by Shardik »

Wolfram wrote:Here it is directly changed, but now it isn't round anymore.
According to Apple's documentation Wilbert's method doesn't work with rounded NSSearchFields:
[color=#0040FF][u]Apple's API reference for drawsBackground[/u][/color] wrote:When the value of this property is YES, the cell draws its background color. In order to prevent inconsistent rendering, background color rendering is automatically disabled for rounded-bezel text fields.

You have to subclass the NSSeachFieldCell and set a callback on drawInteriorWithFrame:inView: to change the background color. Unfortunately Apple has changed some internals of the NSSearchField beginning with Yosemite, so that the following example currently only works up to Mavericks. I have tested the following example with PB 5.44 x86 and x64 in ASCII and Unicode mode on these MacOS versions (+ works, - doesn't work):
+ 10.6.8 (Snow Leopard, only x86)
+ 10.7.5 (Lion)
+ 10.9.5 (Mavericks)
- 10.10.5 (Yosemite)
- 10.12.3 (Sierra)

Code: Select all

EnableExplicit

Define ContentView.I
Define Frame.NSRect
Define SearchFieldCell.I
Define SearchField.I
Define SubclassedSearchFieldCell.I

ProcedureC SubclassedSearchFieldCellCallback(Object.I, Selector.I,
  x86.F, y86.F, Width86.F, Height86.F, ParentView.I, D1.D, D2.D, D3.D, D4.D,
  x64.D, y64.D, Width64.D, Height64.D)
  Protected CellFrame.NSRect
  Protected BackColor.I
  Protected Radius.CGFloat
  Protected SearchFieldWidth.CGFloat

  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    CellFrame\origin\x = x64 + 1
    CellFrame\origin\y = y64 + 3
    CellFrame\size\width = Width64 - 2
    CellFrame\size\height = Height64 - 5
  CompilerElse
    CellFrame\origin\x = x86 + 1
    CellFrame\origin\y = y86 + 3
    CellFrame\size\width = Width86 - 2
    CellFrame\size\height = Height86 - 5
  CompilerEndIf

  BackColor = CocoaMessage(0, 0, "NSColor yellowColor")
  Radius = CellFrame\size\height / 2.0
  CocoaMessage(0, BackColor, "setFill")
  CocoaMessage(0, CocoaMessage(0, 0,
    "NSBezierPath bezierPathWithRoundedRect:@", @CellFrame,
    "xRadius:@", @Radius,
    "yRadius:@", @Radius),
    "fill")

  ; ----- Draw search button
  SearchFieldWidth = CellFrame\size\width
  CellFrame\size\width = 20
  CocoaMessage(0, CocoaMessage(0, Object, "searchButtonCell"),
    "drawInteriorWithFrame:@", @CellFrame,
    "inView:", ParentView)

  ; ----- Draw cancel button
  CellFrame\origin\x = SearchFieldWidth - 20
  CocoaMessage(0, CocoaMessage(0, Object, "cancelButtonCell"),
    "drawInteriorWithFrame:@", @CellFrame,
    "inView:", ParentView)
EndProcedure

OpenWindow(0, 270, 100, 220, 50, "Colored NSSearchField")
 
; ----- Create NSSearchField
Frame\origin\x = 10
Frame\origin\y = 10
Frame\size\width = WindowWidth(0) - 20
Frame\size\height = 25
SearchField = CocoaMessage(0, 0, "NSSearchField alloc")
CocoaMessage(0, SearchField, "initWithFrame:@", @Frame)
ContentView = CocoaMessage(0, WindowID(0), "contentView")
CocoaMessage(0, ContentView, "addSubview:", SearchField)

; ----- Subclass NSSearchFieldCell
SearchFieldCell = CocoaMessage(0, SearchField, "cell")
SubclassedSearchFieldCell = objc_allocateClassPair_(CocoaMessage(0,
  SearchFieldCell, "class"), "SubclassedSearchFieldCell", 0)
objc_registerClassPair_(SubclassedSearchFieldCell)
object_setClass_(SearchFieldCell, SubclassedSearchFieldCell)
class_addMethod_(SubclassedSearchFieldCell,
  sel_registerName_("drawInteriorWithFrame:inView:"),
  @SubclassedSearchFieldCellCallback(), "v@:@@")

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Last edited by Shardik on Sun Feb 19, 2017 2:47 pm, edited 1 time in total.
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: How to change color of NSSearchField?

Post by Wolfram »

Nice!
But now the search string isn't shown if the search field lost it's focus.

Do you have any idea?
macOS Catalina 10.15.7
User avatar
Shardik
Addict
Addict
Posts: 1988
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: How to change color of NSSearchField?

Post by Shardik »

Wolfram wrote:But now the search string isn't shown if the search field lost it's focus.

Do you have any idea?
The following new code example displays the search string even when the search field lost it's focus (tested successfully on MacOS 10.6.8 'Snow Leopard' with PB 5.44 x86 in ASCII and Unicode mode and on MacOS 10.7.5 'Lion' with PB 5.44 x86 and x64 in ASCII and Unicode mode):

Code: Select all

EnableExplicit

#NSFocusRingOnly = 0

ImportC ""
  NSRectFill(*Rect.NSRect)
  NSSetFocusRingStyle(FocusRingPlacement.I)
EndImport

Define Frame.NSRect
Define SearchField.I
Define SearchFieldCell.I
Define SubclassedSearchFieldCell.I

ProcedureC SubclassedSearchFieldCellCallback(SearchFieldCell.I, Selector.I,
  x86.F, y86.F, Width86.F, Height86.F, SearchField.I, D1.D, D2.D, D3.D, D4.D,
  x64.D, y64.D, Width64.D, Height64.D)
  Protected BackColor.I
  Protected CellFrame.NSRect
  Protected Radius.CGFloat

  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    CellFrame\origin\x = x64 + 1
    CellFrame\origin\y = y64 + 3
    CellFrame\size\width = Width64 - 2
    CellFrame\size\height = Height64 - 5
  CompilerElse
    CellFrame\origin\x = x86 + 1
    CellFrame\origin\y = y86 + 3
    CellFrame\size\width = Width86 - 2
    CellFrame\size\height = Height86 - 5
  CompilerEndIf

  ; ----- Fill in back color
  BackColor = CocoaMessage(0, 0, "NSColor yellowColor")
  Radius = CellFrame\size\height / 2.0
  CocoaMessage(0, BackColor, "setFill")
  CocoaMessage(0, CocoaMessage(0, 0,
    "NSBezierPath bezierPathWithRoundedRect:@", @CellFrame,
    "xRadius:@", @Radius,
    "yRadius:@", @Radius),
    "fill")

  ; ----- Draw text content and buttons
  CellFrame\origin\x - 1
  CocoaMessage(0, SearchFieldCell,
    "drawInteriorWithFrame:@", @CellFrame,
    "inView:", SearchField)

  ; ----- Draw border around search field
  Radius + 1
  CocoaMessage(0, CocoaMessage(0, 0, "NSColor lightGrayColor"), "setStroke")
  CocoaMessage(0, CocoaMessage(0, 0,
    "NSBezierPath bezierPathWithRoundedRect:@", @CellFrame,
    "xRadius:@", @Radius,
    "yRadius:@", @Radius),
    "stroke")

  If CocoaMessage(0, SearchFieldCell, "showsFirstResponder")
    ; ----- Draw focus ring around search field
    CocoaMessage(0, 0, "NSGraphicsContext saveGraphicsState")
    NSSetFocusRingStyle(#NSFocusRingOnly)
    CocoaMessage(0, CocoaMessage(0, 0,
      "NSBezierPath bezierPathWithRoundedRect:@", @CellFrame,
      "xRadius:@", @Radius,
      "yRadius:@", @Radius),
      "fill")
    CocoaMessage(0, 0, "NSGraphicsContext restoreGraphicsState")
  EndIf
EndProcedure

OpenWindow(0, 270, 100, 220, 75, "Colored NSSearchField")
StringGadget(0, 70, 40, 80, 25, "")

; ----- Create NSSearchField
Frame\origin\x = 10
Frame\origin\y = 10
Frame\size\width = WindowWidth(0) - 20
Frame\size\height = 25
SearchField = CocoaMessage(0, 0, "NSSearchField alloc")
CocoaMessage(0, SearchField, "initWithFrame:@", @Frame)
CocoaMessage(0, CocoaMessage(0, WindowID(0), "contentView"),
  "addSubview:", SearchField)

; ----- Get NSSearchFieldCell
SearchFieldCell = CocoaMessage(0, SearchField, "cell")

; ----- Subclass NSSearchFieldCell
SubclassedSearchFieldCell = objc_allocateClassPair_(CocoaMessage(0,
  SearchFieldCell, "class"), "SubclassedSearchFieldCell", 0)
object_setClass_(SearchFieldCell, SubclassedSearchFieldCell)
objc_registerClassPair_(SubclassedSearchFieldCell)

; ----- Set callback in SearchFieldCell for method drawWithFrame:inView:
class_addMethod_(SubclassedSearchFieldCell,
  sel_registerName_("drawWithFrame:inView:"),
  @SubclassedSearchFieldCellCallback(), "v@:@@")

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: How to change color of NSSearchField?

Post by Wolfram »

Unfortunately these code does't work anymore.
It works on OSX 10.7.5, PB 5.46, Xcode 4.6.3, but not on OSX 10.13.6, PB 5.46 /5.62, Xcode 8.3.3 / 10.
The callback is't called anymore.

Dose someone have a idea for a reason?

As Shardik says Apple made some changes on the NSSearchField, but I could find out what?
macOS Catalina 10.15.7
User avatar
Shardik
Addict
Addict
Posts: 1988
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: How to change color of NSSearchField?

Post by Shardik »

The following example works on MacOS 10.13.6 'High Sierra' with Xcode 10.1 and PB 5.46 x64 in ASCII and Unicode mode:

Code: Select all

EnableExplicit

Define ContentView.I
Define Rect.NSRect
Define SearchField.I

Procedure SetBackColor(SearchField.I, BackColor.I)
  Protected Alpha.CGFloat
  Protected Blue.CGFloat
  Protected CIColor.I
  Protected Filter.I
  Protected FilterArray.I
  Protected Green.CGFloat
  Protected Red.CGFloat

  Alpha = 1
  Red = Red(BackColor) / 255
  Green = Green(BackColor) / 255
  Blue = Blue(BackColor) / 255
  Filter = CocoaMessage(0, 0, "CIFilter filterWithName:$", @"CIFalseColor")
  CocoaMessage(0, Filter, "setDefaults")
  CIColor = CocoaMessage(0, 0, "CIColor colorWithRed:@", @Red,
    "green:@", @Green, "blue:@", @Blue)
  CocoaMessage(0, Filter,
    "setValue:", CIColor,
    "forKey:$", @"inputColor1")
  FilterArray = CocoaMessage(0, 0, "NSArray arrayWithObject:", Filter)
  CocoaMessage(0, SearchField, "setWantsLayer:", #YES)
  CocoaMessage(0, SearchField, "setContentFilters:", FilterArray)
EndProcedure

OpenWindow(0, 270, 100, 220, 45, "Colored NSSearchField")

; ----- Create NSSearchField
Rect\origin\x = 20
Rect\origin\y = 10
Rect\size\width = WindowWidth(0) - 40
Rect\size\height = 25
SearchField = CocoaMessage(0, 0, "NSSearchField alloc")
CocoaMessage(0, SearchField, "initWithFrame:@", @Rect)
ContentView = CocoaMessage(0, WindowID(0), "contentView")
CocoaMessage(0, ContentView, "addSubview:", SearchField)
SetBackColor(SearchField, $FFFF00)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Post Reply