Archive for the 'NSMenuItem' Category

2017/07/16 ASOCでcolor popup buttonを作成

動的にWindow+View+popup buttonを作成し、色選択を行うpopup menuを作成するAppleScriptです。

color_popup.png

Script Editor上でControl-Command-Rと操作することで実行できます。アプレットとして保存して実行してもOKです。

color_popup2.png

いくつか候補がある色のうちからどれかを選ぶといった処理は割とあるのですが、色のプレビューを行うインタフェースはAppleScriptのデフォルトのコマンドでは用意されていないので、ちょっと作ってみました。

たとえば、テキストエディット上で資料の文章をチェックを行なっているような場合、見直す必要のある箇所に赤く色をつけておいて、赤くマークした箇所のみAppleScriptで抽出するとかいう処理は便利にやっています(ものすごく効率がいい)。赤以外でもマークした箇所を抽出するとかいったら、本Scriptのような部品を利用して「どの色でマークした箇所を抽出しようか」文章中の色付き部分を全部スキャンしたうえで色選択という処理ができるわけです。

Script EditorおよびASObjC Explorer 4上での実行は確認できていますが、Script Debugger 6.0.5 試用版ではクラッシュします。

1枚のみのWindowを作成する場合にはNSWindowControllerを用いる必要はないようですが、ためしにNSWindowControllerを省いたScriptも作って試してみたところ・・・安定性がやや損なわれるようでした。結局、NSWindowControllerがあったほうがよいのでは???

指定色でNSImageを作成してmenu itemに設定していますが、macOS 10.13ではこれが正方形でないとおもいどおりのサイズでメニューに表示されませんでした。

color_popup1013.png
▲本ScriptをmacOS 10.13で動かしたときのイメージ(10.12上で再現)

color_popup1013b.png
▲macOS 10.13上のpopup menu上で大きくColor部分を表示するときには、imageの縦横サイズを同じに?

AppleScript名:ASOCでcolor popup buttonを作成
– Created 2017-07-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “Carbon” – AEInteractWithUser() is in Carbon
–http://piyocast.com/as/archives/4724

property windisp : false
property wController : false –いらなかったかも?

if current application’s AEInteractWithUser(-1, missing value, missing value) is not equal to 0 then return

set ap1List to {{65535, 0, 65535}, {0, 32896, 16448}, {0, 32896, 65535}, {19702, 31223, 40505}}

set aButtonMSG to “OK”
set aSliderValMSG to “Select Color”
set aVal to getPopupValues(ap1List, 65535, aButtonMSG, aSliderValMSG, 20) of me

on getPopupValues(ap1List, aColMax, aButtonMSG, aSliderValMSG, timeOutSecs)
  
  
set (my windisp) to true
  
  
set aView to current application’s NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 360, 100))
  
  
–Labelをつくる
  
set a1TF to current application’s NSTextField’s alloc()’s initWithFrame:(current application’s NSMakeRect(30, 60, 80, 20))
  
a1TF’s setEditable:false
  
a1TF’s setStringValue:“Color:”
  
a1TF’s setDrawsBackground:false
  
a1TF’s setBordered:false
  
  
–Ppopup Buttonをつくる
  
set a1Button to current application’s NSPopUpButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(80, 60, 200, 20)) pullsDown:false
  
a1Button’s removeAllItems()
  
  
set a1Menu to current application’s NSMenu’s alloc()’s init()
  
  
set iCount to 0
  
repeat with i in ap1List
    copy i to {r1, g1, b1}
    
    
set nsCol to makeNSColorFromRGBAval(r1, g1, b1, aColMax, aColMax) of me
    
set anImage to makeNSImageWithFilledWithColor(64, 16, nsCol) of me
    
    
set aTitle to “col_test_” & (iCount as string)
    
set aMenuItem to (current application’s NSMenuItem’s alloc()’s initWithTitle:aTitle action:“actionHandler:” keyEquivalent:“”)
    (
aMenuItem’s setImage:anImage)
    (
aMenuItem’s setEnabled:true)
    (
a1Menu’s addItem:aMenuItem)
    
    
set iCount to iCount + 1
  end repeat
  
  
a1Button’s setMenu:a1Menu
  
  
  
–Buttonをつくる
  
set bButton to (current application’s NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(80, 10, 140, 40)))
  
bButton’s setButtonType:(current application’s NSMomentaryLightButton)
  
bButton’s setBezelStyle:(current application’s NSRoundedBezelStyle)
  
bButton’s setTitle:aButtonMSG
  
bButton’s setTarget:me
  
bButton’s setAction:(“clicked:”)
  
bButton’s setKeyEquivalent:(return)
  
  
aView’s addSubview:a1TF
  
  
aView’s addSubview:a1Button
  
aView’s addSubview:bButton
  
aView’s setNeedsDisplay:true
  
  
–NSWindowControllerを作ってみた(いらない?)
  
set aWin to (my makeWinWithView(aView, 300, 100, aSliderValMSG))
  
  
set wController to current application’s NSWindowController’s alloc()
  
wController’s initWithWindow:aWin
  
  
wController’s showWindow:me
  
  
set aCount to timeOutSecs * 100
  
  
set hitF to false
  
repeat aCount times
    if (my windisp) = false then
      set hitF to true
      
exit repeat
    end if
    
delay 0.01
    
set aCount to aCount - 1
  end repeat
  
  
my closeWin:aWin
  
  
if hitF = true then
    set s1Val to a1Button’s titleOfSelectedItem() as string
  else
    set s1Val to false
  end if
  
  
return s1Val
  
end getPopupValues

on clicked:aSender
  set (my windisp) to false
end clicked:

–make Window for Display
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle)
  set aScreen to current application’s NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
  
set aBacking to current application’s NSTitledWindowMask
  
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
– Window
  
set aWin to current application’s NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
–aWin’s setBackgroundColor:(current application’s NSColor’s whiteColor())
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(current application’s NSNormalWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
–aWin’s makeKeyAndOrderFront:(me)
  
  
aWin’s setContentView:aView
  
  
return aWin
  
end makeWinWithView

–close win
on closeWin:aWindow
  repeat with n from 10 to 1 by -1
    (aWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
aWindow’s |close|()
end closeWin:

–Popup Action Handler
on actionHandler:sender
  set aTag to tag of sender as integer
  
set aTitle to title of sender as string
end actionHandler:

on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
  set aRedCocoa to (redValue / aMaxVal) as real
  
set aGreenCocoa to (greenValue / aMaxVal) as real
  
set aBlueCocoa to (blueValue / aMaxVal) as real
  
set aAlphaCocoa to (alphaValue / aMaxVal) as real
  
set aColor to current application’s NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
  
return aColor
end makeNSColorFromRGBAval

–指定サイズの画像を作成し、指定色で塗ってファイル書き出し
on makeNSImageWithFilledWithColor(aWidth, aHeight, fillColor)
  set anImage to current application’s NSImage’s alloc()’s initWithSize:(current application’s NSMakeSize(aWidth, aHeight))
  
anImage’s lockFocus()
  

  
set theRect to {{x:0, y:0}, {height:aHeight, width:aWidth}}
  
set theNSBezierPath to current application’s NSBezierPath’s bezierPath
  
theNSBezierPath’s appendBezierPathWithRect:theRect
  

  
fillColor’s |set|() –色設定
  
theNSBezierPath’s fill() –ぬりつぶし
  

  
anImage’s unlockFocus()
  

  
return anImage
end makeNSImageWithFilledWithColor

★Click Here to Open This Script 

2013/10/10 AppleScriptObjCで複数項目選択可能なポップアップボタンを

AppleScriptObjCのプログラムで、複数項目が選択可能なポップアップボタンを作ってみました。

作ってみましたといっても、ポップアップボタンは、ボタンをクリックすると表示されるメニュー項目のうち、選択状態にあるものをボタンのタイトルとして表示するというものであり、複数項目の選択はできません。

mpop9.png

できないながらも、どーにかして複数項目選択できるようにしなくてはならない(このへん、海外のクライアントの要望)ということで、NSPopupButtonをそのままで使うことは無理です。

そこで、NSPopupButtonをクリックすると表示されるメニューの方をなんとかしてしまえ、ということになります(このあたりまでは、海外のサイトでもよく話が出ている)。

メニューとしてNSCustomViewとかNSBoxあたりを指定する、というのがだいたいのオチなのですが……実際に試してみると、言うほど簡単ではありません。

さらにメニューに表示させる項目の数を動的に変化させたいという話もあり、メニューとして表示させるビューの大きさの制御とか、NSMatrixの位置をNSCustomViewやNSBox内のどこを基準に寄せるかといった話をクリアしなくてはなりません。

popup1.png

結局、それが面倒だったので、NSMatrixを直接メニューとして指定して表示させるようにしてみました。

popup2.png

これなら、NSMatrixを載せている「下地」との位置関係やサイズの調整などを一切行わなくてすむために、楽にできました。

そこまでクリアしても、まだ大変なことが続きます。

メニューで複数項目を選択できるところまではなんとかクリアしたのですが、その選択内容をポップアップボタンのタイトルに指定すると……メニュー内に新たな選択項目として追加されてしまうのでした。

……いろいろ思い悩んだ結果、クライアントには「勘弁してください」と泣きつき、この「複数項目選択可能なポップアップボタン」はお蔵入りに。

正確にいうと、ポップアップボタンにわざわざしなくても、ウィンドウ上にふつーにチェックボックスとして並べれば、その方が確実だしよいのではないか、という提案を行いました。

ところが、その翌日にはなんとか動くところまで持って行けました。結局、ポップアップボタンに選択状態を表示させるとメニューに追加されてしまうので、選択内容を別の部品に表示させることに。

枠や下地の描画をやめて、マウスクリックを無視するように設定して、ポップアップボタンの上に載せておけば、まるでポップアップボタンに選択状態を表示している……かのように見えるはずです。

mpop1.png
▲ビルドして起動したところ

mpop2.png
▲「+」ボタンをクリックしてテーブル内の行を追加し、それぞれにデータを入力

mpop3.png
▲ひととおりメニューに表示する項目を入力し終わったら、「Reflect」ボタンをクリック。これで、テーブルの内容をもとにポップアップメニューを動的に作成

mpop4.png
▲テーブル内容をもとに作成されたポップアップメニュー

mpop6.png
▲メニュー内の複数項目を選択できる

mpop7.png
▲複数選択した状態を別のテキストフィールド上に表示

→ Xcodeプロジェクトのダウンロード(69KB)

AppleScriptObjCファイル名:AppDelegate.applescript

– AppDelegate.applescript
– ViewInMenu

– Created by Takaaki Naganoya on 2013/10/08.
– Copyright (c) 2013年 Takaaki Naganoya. All rights reserved.


script AppDelegate
  property parent : class “NSObject”
  
  property pu : missing value – IBOutlet for the popUpButton
  
property item1 : missing value
  
property mat : missing value – IBOutlet for the matrix of NSLabels
  
  property theData : {} – the table data
  
property theArrayController : missing value
  
property aTable : missing value
  
  property aTextF : missing value
  
  
  
  
  
  on applicationWillFinishLaunching_(aNotification)
    
    set theData to {}
    
    set item1 to current application’s NSMenuItem’s alloc()’s initWithTitle_action_keyEquivalent_(“”, missing value, “”)
    
item1’s setView_(mat)
    
item1’s setTarget_(me)
    
pu’s removeAllItems()
    
pu’s |menu|()’s addItem_(item1)
    
    aTextF’s setStringValue_(“”)
    
  end applicationWillFinishLaunching_
  
  
  
  
  
  on applicationShouldTerminate_(sender)
    return current application’s NSTerminateNow
  end applicationShouldTerminate_
  
  
  
  
  on matrixClick_(sender) – IBAction for the matrix
    
    aTextF’s setStringValue_(“”)
    
    tell theArrayController
      set outList to arrangedObjects() as list
    end tell
    
    set aLen to length of outList
    
set aList to {}
    
    
    repeat with i from 0 to (aLen - 1)
      
      set aTmpVal to mat’s cellAtRow_column_(i, 0)’s integerValue()
      
set aTmpVal to aTmpVal as number
      
      if aTmpVal is equal to 1 then
        
        set aTmpText to mat’s cellAtRow_column_(i, 0)’s title()
        
set the end of aList to aTmpText
        
      end if
      
    end repeat
    
    set aText to retArrowText__(aList, “, “)
    
set aText to aText as string
    
    aTextF’s setStringValue_(aText)
    
  end matrixClick_
  
  
  
  
  –Array Controllerから動的にメニューを作成する
  
on buttonClick_(sender) – IBAction for the button
    
    set outList to {}
    
    tell theArrayController
      set theList to arrangedObjects() as list
    end tell
    
    log {“theList”, theList}
    
    repeat with i in theList
      set j to contents of i
      
set jj to aTitle of j
      
set jj to jj as string
      
set the end of outList to jj
    end repeat
    
    set aLen to length of outList
    
    
    mat’s renewRows_columns_(aLen, 1)
    
mat’s sizeToCells()
    
    repeat with i from 0 to (aLen - 1)
      set aStr to contents of item (i + 1) of outList
      
set aStr to aStr as string
      
mat’s cellAtRow_column_(i, 0)’s setTitle_(aStr)
    end repeat
    
    
    aTextF’s setStringValue_(“”)
    
  end buttonClick_
  
  
  –リストを任意のデリミタ付きでテキストに
  
on retArrowText__(aList, aDelim)
    
    set aText to “”
    
set aLen to length of aList
    
set aCount to 1
    
    repeat with i in aList
      set j to i as string
      
      if aCount < aLen then
        set aText to aText & j & aDelim
      else
        set aText to aText & j
      end if
      
      set aCount to aCount + 1
      
    end repeat
    
    return aText
    
  end retArrowText__
  
  
  
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に