Xamarin.Mac의 메뉴
이 문서에서는 Xamarin.Mac 애플리케이션의 메뉴 작업에 대해 설명합니다. Xcode 및 Interface Builder에서 메뉴 및 메뉴 항목을 만들고 기본 프로그래밍 방식으로 작업하는 방법에 대해 설명합니다.
Xamarin.Mac 애플리케이션에서 C# 및 .NET으로 작업하는 경우 개발자와 Xcode에서 작업하는 것과 동일한 Cocoa 메뉴에 Objective-C 액세스할 수 있습니다. Xamarin.Mac은 Xcode와 직접 통합되므로 Xcode의 인터페이스 작성기를 사용하여 메뉴 모음, 메뉴 및 메뉴 항목을 만들고 기본(또는 필요에 따라 C# 코드에서 직접 만들 수 있음)할 수 있습니다.
메뉴는 Mac 애플리케이션의 사용자 환경의 필수적인 부분이며 일반적으로 사용자 인터페이스의 다양한 부분에 표시됩니다.
- 애플리케이션의 메뉴 모음 - 모든 Mac 애플리케이션의 화면 맨 위에 표시되는 기본 메뉴입니다.
- 상황에 맞는 메뉴 - 사용자가 창에서 항목을 마우스 오른쪽 단추로 클릭하거나 컨트롤을 클릭할 때 표시됩니다.
- 상태 막대 - 화면 위쪽(메뉴 모음 시계의 왼쪽)에 표시되고 항목이 추가되면 왼쪽으로 증가하는 애플리케이션 메뉴 모음의 맨 오른쪽에 있는 영역입니다.
- 도킹 메뉴 - 사용자가 응용 프로그램의 아이콘을 마우스 오른쪽 단추로 클릭하거나 컨트롤을 클릭하거나 사용자가 아이콘을 마우스 왼쪽 단추로 클릭하고 마우스 단추를 누를 때 나타나는 도크의 각 애플리케이션에 대한 메뉴입니다.
- 팝업 단추 및 풀다운 목록 - 팝업 단추는 선택한 항목을 표시하고 사용자가 클릭할 때 선택할 수 있는 옵션 목록을 제공합니다. 풀다운 목록은 일반적으로 현재 작업의 컨텍스트와 관련된 명령을 선택하는 데 사용되는 팝업 단추의 유형입니다. 둘 다 창의 아무 곳에나 나타날 수 있습니다.
이 문서에서는 Xamarin.Mac 애플리케이션에서 Cocoa 메뉴 모음, 메뉴 및 메뉴 항목을 사용하는 기본 사항을 설명합니다. 이 문서에서 사용할 주요 개념과 기술을 다루므로 Hello, Mac 문서, 특히 Xcode 및 인터페이스 작성기 및 콘센트 및 작업 소개 섹션을 통해 작업하는 것이 좋습니다.
Xamarin.Mac Internals 문서의 섹션에 Objective-C대한 C# 클래스/메서드 노출을 살펴보고 C# 클래스 Objective-C 를 개체 및 UI 요소에 연결하는 데 사용되는 특성과 Export
특성을 설명 Register
합니다.
애플리케이션의 메뉴 모음
모든 창에 자체 메뉴 모음을 연결할 수 있는 Windows OS에서 실행되는 애플리케이션과 달리 macOS에서 실행되는 모든 애플리케이션에는 해당 애플리케이션의 모든 창에 사용되는 화면 위쪽을 따라 실행되는 단일 메뉴 모음이 있습니다.
이 메뉴 모음의 항목은 지정된 순간에 애플리케이션 및 해당 사용자 인터페이스의 현재 컨텍스트 또는 상태에 따라 활성화되거나 비활성화됩니다. 예를 들어 사용자가 텍스트 필드를 선택하면 편집 메뉴의 항목(예: 복사 및 잘라내기)이 활성화됩니다.
Apple 및 기본적으로 모든 macOS 애플리케이션에는 애플리케이션의 메뉴 모음에 표시되는 메뉴 및 메뉴 항목의 표준 집합이 있습니다.
- Apple 메뉴 - 이 메뉴는 실행 중인 애플리케이션에 관계없이 항상 사용자가 사용할 수 있는 시스템 전체 항목에 대한 액세스를 제공합니다. 개발자는 이러한 항목을 수정할 수 없습니다.
- 앱 메뉴 - 이 메뉴는 애플리케이션의 이름을 굵게 표시하고 사용자가 현재 실행 중인 애플리케이션을 식별하는 데 도움이 됩니다. 응용 프로그램 종료와 같이 지정된 문서나 프로세스가 아닌 애플리케이션 전체에 적용되는 항목이 포함됩니다.
- 파일 메뉴 - 애플리케이션이 작동하는 문서를 만들거나 열거나 저장하는 데 사용되는 항목입니다. 애플리케이션이 문서 기반이 아닌 경우 이 메뉴의 이름을 바꾸거나 제거할 수 있습니다.
- 편집 메뉴 - 애플리케이션의 사용자 인터페이스에서 요소를 편집하거나 수정하는 데 사용되는 잘라내기, 복사 및 붙여넣기와 같은 명령을 포함합니다.
- 서식 메뉴 - 애플리케이션이 텍스트로 작동하는 경우 이 메뉴에는 해당 텍스트의 서식을 조정하는 명령이 포함됩니다.
- 보기 메뉴 - 애플리케이션의 사용자 인터페이스에서 콘텐츠가 표시되는 방식(보기)에 영향을 주는 명령을 보유합니다.
- 애플리케이션별 메뉴 - 애플리케이션과 관련된 모든 메뉴(예: 웹 브라우저의 책갈피 메뉴)입니다. 표시줄의 보기 및 창 메뉴 사이에 표시됩니다.
- 창 메뉴 - 애플리케이션의 창 작업 명령과 현재 열려 있는 창 목록이 포함되어 있습니다.
- 도움말 메뉴 - 애플리케이션에서 화면 도움말을 제공하는 경우 도움말 메뉴는 바에서 가장 적합한 메뉴여야 합니다.
애플리케이션 메뉴 모음 및 표준 메뉴 및 메뉴 항목에 대한 자세한 내용은 Apple의 휴먼 인터페이스 지침을 참조하세요.
기본 애플리케이션 메뉴 모음
새 Xamarin.Mac 프로젝트를 만들 때마다 macOS 애플리케이션에 일반적으로 포함되는 일반적인 항목이 있는 표준 기본 애플리케이션 메뉴 모음이 자동으로 제공됩니다(위의 섹션에서 설명한 대로). 애플리케이션의 기본 메뉴 모음은 Solution Pad의 프로젝트 아래에 있는 Main.storyboard 파일(앱의 나머지 UI와 함께)에 정의됩니다.
Main.storyboard 파일을 두 번 클릭하여 Xcode의 인터페이스 작성기에서 편집할 수 있도록 열면 메뉴 편집기 인터페이스가 표시됩니다.
여기에서 파일 메뉴에서 열기 메뉴 항목과 같은 항목을 클릭하고 특성 검사기에서 해당 속성을 편집하거나 조정할 수 있습니다.
이 문서의 뒷부분에서 메뉴와 항목을 추가, 편집 및 삭제할 수 있습니다. 지금은 기본적으로 사용할 수 있는 메뉴와 메뉴 항목이 미리 정의된 콘센트 및 작업 집합을 통해 코드에 자동으로 노출되는 방법을 확인하려고 합니다(자세한 내용은 아울렛 및 작업 설명서 참조).
예를 들어 열기 메뉴 항목에 대한 커넥트ion Inspector를 클릭하면 작업에 자동으로 연결되는 것을 openDocument:
볼 수 있습니다.
인터페이스 계층 구조에서 첫 번째 응답자를 선택하고 커넥트ion 검사기에서 아래로 스크롤하면 열기 메뉴 항목이 연결된 작업의 정의 openDocument:
가 표시됩니다(컨트롤에 자동으로 연결되고 자동으로 연결되지 않는 애플리케이션에 대한 몇 가지 다른 기본 작업과 함께).
JEA가 중요한 이유는 무엇일까요? 다음 섹션에서는 이러한 자동 정의 작업이 다른 Cocoa 사용자 인터페이스 요소와 함께 작동하여 메뉴 항목을 자동으로 사용하거나 사용하지 않도록 설정하고 항목에 대한 기본 제공 기능을 제공하는 방법을 알아봅니다.
나중에 이러한 기본 제공 작업을 사용하여 코드에서 항목을 사용하거나 사용하지 않도록 설정하고 선택한 경우 고유한 기능을 제공합니다.
기본 제공 메뉴 기능
UI 항목 또는 코드를 추가하기 전에 새로 만든 Xamarin.Mac 애플리케이션을 실행한 경우 앱 메뉴의 종료 항목과 같이 일부 항목이 자동으로 유선으로 연결되고 사용하도록 설정됩니다(전체 기능이 자동으로 기본 제공됨).
잘라내기, 복사 및 붙여넣기와 같은 다른 메뉴 항목은 다음과 같은 것이 아닙니다.
애플리케이션을 중지하고 Solution Pad에서 Main.storyboard 파일을 두 번 클릭하여 Xcode의 인터페이스 작성기에서 편집할 수 있도록 엽니다. 다음으로, 라이브러리의 텍스트 보기를 인터페이스 편집기의 창 보기 컨트롤러로 끌어옵니다.
제약 조건 편집기에서 텍스트 보기를 창의 가장자리에 고정하고 편집기 맨 위에 있는 4개의 빨간색 I-빔을 모두 클릭하고 4개의 제약 조건 추가 단추를 클릭하여 창이 늘어나고 축소되는 위치를 설정해 보겠습니다.
변경 내용을 사용자 인터페이스 디자인에 저장하고 Mac용 Visual Studio 다시 전환하여 변경 내용을 Xamarin.Mac 프로젝트와 동기화합니다. 이제 애플리케이션을 시작하고, 텍스트 보기에 텍스트를 입력하고, 선택하고, 편집 메뉴를 엽니다.
코드 한 줄을 작성하지 않고도 잘라내기, 복사 및 붙여 넣기 항목이 자동으로 활성화되고 완벽하게 작동하는 방식을 확인합니다.
무슨 일이 일어나고 있는 건가요? 기본 메뉴 항목(위에서 설명한 대로)에 연결된 기본 제공 미리 정의 작업을 기억하세요. macOS의 일부인 대부분의 Cocoa 사용자 인터페이스 요소는 특정 작업(예: copy:
)에 대한 후크를 기본으로 제공합니다. 따라서 창, 활성 및 선택 영역에 추가되면 해당 작업에 연결된 해당 메뉴 항목 또는 항목이 자동으로 사용하도록 설정됩니다. 사용자가 해당 메뉴 항목을 선택하면 개발자 개입 없이 UI 요소에 기본 제공되는 기능이 호출되고 실행됩니다.
메뉴 및 항목 사용 및 사용 안 함
기본적으로 사용자 이벤트가 발생할 NSMenu
때마다 애플리케이션의 컨텍스트에 따라 표시되는 각 메뉴 및 메뉴 항목을 자동으로 사용하거나 사용하지 않도록 설정합니다. 항목을 사용하거나 사용하지 않도록 설정하는 세 가지 방법이 있습니다.
- 자동 메뉴 사용 - 항목이 유선으로 연결된 작업에 응답하는 적절한 개체를 찾을 수 있는 경우
NSMenu
메뉴 항목이 활성화됩니다. 예를 들어 작업에 기본 제공 후크가 있는 위의 텍스트 뷰입니다copy:
. - 사용자 지정 작업 및 validateMenuItem: - 창 또는 보기 컨트롤러 사용자 지정 작업에 바인딩된 메뉴 항목의 경우 작업을 추가하고
validateMenuItem:
메뉴 항목을 수동으로 사용하거나 사용하지 않도록 설정할 수 있습니다. - 수동 메뉴 사용 - 메뉴의 각 항목을 개별적으로 사용하거나 사용하지 않도록 설정하도록 각
NSMenuItem
항목의 속성을 수동으로 설정합니다Enabled
.
시스템을 선택하려면 .의 AutoEnablesItems
NSMenu
속성을 설정합니다. true
는 자동(기본 동작)이며 false
수동입니다.
Important
수동 메뉴를 사용하도록 설정하도록 선택하면 AppKit 클래스 NSTextView
에서 제어하는 메뉴 항목도 자동으로 업데이트되지 않습니다. 코드에서 직접 모든 항목을 사용하도록 설정하고 사용하지 않도록 설정해야 합니다.
validateMenuItem 사용
위에서 설명한 것처럼 창 또는 보기 컨트롤러 사용자 지정 작업에 바인딩된 메뉴 항목의 경우 작업을 추가하고 validateMenuItem:
메뉴 항목을 수동으로 사용하거나 사용하지 않도록 설정할 수 있습니다.
다음 예제 Tag
에서는 속성에서 선택한 텍스트NSTextView
의 상태에 따라 작업에 의해 validateMenuItem:
사용/사용 안 함으로 설정 될 메뉴 항목의 형식을 결정 하는 데 사용 됩니다. 각 Tag
메뉴 항목에 대한 인터페이스 작성기에서 속성이 설정되었습니다.
그리고 뷰 컨트롤러에 추가된 다음 코드는 다음과 같습니다.
[Action("validateMenuItem:")]
public bool ValidateMenuItem (NSMenuItem item) {
// Take action based on the menu item type
// (As specified in its Tag)
switch (item.Tag) {
case 1:
// Wrap menu items should only be available if
// a range of text is selected
return (TextEditor.SelectedRange.Length > 0);
case 2:
// Quote menu items should only be available if
// a range is NOT selected.
return (TextEditor.SelectedRange.Length == 0);
}
return true;
}
이 코드가 실행되고 텍스트가 선택되지 NSTextView
않으면 두 개의 줄 바꿈 메뉴 항목이 비활성화됩니다(보기 컨트롤러의 작업에 연결되더라도).
텍스트 섹션을 선택하고 메뉴를 다시 열면 두 개의 줄 바꿈 메뉴 항목을 사용할 수 있습니다.
코드의 메뉴 항목 사용 및 응답
위에서 설명한 것처럼 특정 Cocoa 사용자 인터페이스 요소를 UI 디자인(예: 텍스트 필드)에 추가하는 것만으로도 코드를 작성하지 않고도 몇 가지 기본 메뉴 항목이 사용하도록 설정되고 자동으로 작동합니다. 다음으로 Xamarin.Mac 프로젝트에 자체 C# 코드를 추가하여 메뉴 항목을 사용하도록 설정하고 사용자가 선택할 때 기능을 제공하는 방법을 살펴보겠습니다.
예를 들어 사용자가 파일 메뉴에서 열기 항목을 사용하여 폴더를 선택할 수 있도록 하겠습니다. 이 함수가 애플리케이션 전체 함수가 되고 제공 창 또는 UI 요소로 제한되지 않기를 원하므로 이를 처리하는 코드를 애플리케이션 대리자에게 추가하겠습니다.
Solution Pad에서 파일을 두 번 클릭하여 AppDelegate.CS
편집용으로 엽니다.
DidFinishLaunching
메서드 아래에 다음 코드를 추가합니다.
[Export ("openDocument:")]
void OpenDialog (NSObject sender)
{
var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = false;
dlg.CanChooseDirectories = true;
if (dlg.RunModal () == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Informational,
InformativeText = "At this point we should do something with the folder that the user just selected in the Open File Dialog box...",
MessageText = "Folder Selected"
};
alert.RunModal ();
}
}
이제 애플리케이션을 실행하고 파일 메뉴를 엽니다.
이제 열기 메뉴 항목을 사용할 수 있습니다. 이 옵션을 선택하면 열려 있는 대화 상자가 표시됩니다.
열기 단추를 클릭하면 경고 메시지가 표시됩니다.
여기서 핵심 줄은 [Export ("openDocument:")]
AppDelegate에 작업에 응답 openDocument:
하는 메서드 void OpenDialog (NSObject sender)
가 있음을 알려줍니다NSMenu
. 위에서 기억해야 하는 경우 Open 메뉴 항목은 인터페이스 작성기에서 기본적으로 이 작업에 자동으로 연결됩니다.
다음으로, 고유한 메뉴, 메뉴 항목 및 작업을 만들고 코드에서 이에 응답하는 방법을 살펴보겠습니다.
최근 메뉴 열기 작업
기본적으로 파일 메뉴에는 사용자가 앱에서 연 마지막 여러 파일을 추적하는 최근 항목 열기가 포함되어 있습니다. 기반 Xamarin.Mac 앱을 만드는 NSDocument
경우 이 메뉴가 자동으로 처리됩니다. 다른 유형의 Xamarin.Mac 앱의 경우 이 메뉴 항목을 수동으로 관리하고 응답해야 합니다.
최근에 사용한 항목 열기 메뉴를 수동으로 처리하려면 먼저 다음을 사용하여 새 파일을 열거나 저장했음을 알려야 합니다.
// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);
앱에서 사용하지 NSDocuments
않더라도 파일의 위치와 함께 메서드로 전송 NSUrl
하여 기본 최근 항목 열기 메뉴를 확인합니다 NoteNewRecentDocumentURL
SharedDocumentController
.NSDocumentController
다음으로 앱 대리자의 메서드를 OpenFile
재정의하여 사용자가 최근에 사용한 항목 열기 메뉴에서 선택한 파일을 열어야 합니다. 예시:
public override bool OpenFile (NSApplication sender, string filename)
{
// Trap all errors
try {
filename = filename.Replace (" ", "%20");
var url = new NSUrl ("file://"+filename);
return OpenFile(url);
} catch {
return false;
}
}
파일을 열 수 있으면 반환 true
하고, 그렇지 않으면 파일을 false
열 수 없다는 기본 제공 경고가 사용자에게 표시됩니다.
[최근 항목 열기] 메뉴에서 반환된 파일 이름 및 경로에 공백이 포함될 수 있으므로 이 문자를 만들기 전에 이 문자를 제대로 이스케이프 NSUrl
해야 합니다. 그렇지 않으면 오류가 발생합니다. 다음 코드를 사용하여 이 작업을 수행합니다.
filename = filename.Replace (" ", "%20");
마지막으로 파일을 가리키는 파일을 만들고 NSUrl
앱 대리자의 도우미 메서드를 사용하여 새 창을 열고 파일을 로드합니다.
var url = new NSUrl ("file://"+filename);
return OpenFile(url);
모든 항목을 함께 끌어오려면 AppDelegate.cs 파일의 예제 구현을 살펴보겠습니다.
using AppKit;
using Foundation;
using System.IO;
using System;
namespace MacHyperlink
{
[Register ("AppDelegate")]
public class AppDelegate : NSApplicationDelegate
{
#region Computed Properties
public int NewWindowNumber { get; set;} = -1;
#endregion
#region Constructors
public AppDelegate ()
{
}
#endregion
#region Override Methods
public override void DidFinishLaunching (NSNotification notification)
{
// Insert code here to initialize your application
}
public override void WillTerminate (NSNotification notification)
{
// Insert code here to tear down your application
}
public override bool OpenFile (NSApplication sender, string filename)
{
// Trap all errors
try {
filename = filename.Replace (" ", "%20");
var url = new NSUrl ("file://"+filename);
return OpenFile(url);
} catch {
return false;
}
}
#endregion
#region Private Methods
private bool OpenFile(NSUrl url) {
var good = false;
// Trap all errors
try {
var path = url.Path;
// Is the file already open?
for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
if (content != null && path == content.FilePath) {
// Bring window to front
NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
return true;
}
}
// Get new window
var storyboard = NSStoryboard.FromName ("Main", null);
var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;
// Display
controller.ShowWindow(this);
// Load the text into the window
var viewController = controller.Window.ContentViewController as ViewController;
viewController.Text = File.ReadAllText(path);
viewController.SetLanguageFromPath(path);
viewController.View.Window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
viewController.View.Window.RepresentedUrl = url;
// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);
// Make as successful
good = true;
} catch {
// Mark as bad file on error
good = false;
}
// Return results
return good;
}
#endregion
#region actions
[Export ("openDocument:")]
void OpenDialog (NSObject sender)
{
var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = true;
dlg.CanChooseDirectories = false;
if (dlg.RunModal () == 1) {
// Nab the first file
var url = dlg.Urls [0];
if (url != null) {
// Open the document in a new window
OpenFile (url);
}
}
}
#endregion
}
}
앱의 요구 사항에 따라 사용자가 두 개 이상의 창에서 동일한 파일을 동시에 열지 않도록 할 수 있습니다. 예제 앱에서 사용자가 이미 열려 있는 파일(최근 항목 열기 또는 열기.. 메뉴 항목 중 하나)을 선택하면 파일이 포함된 창이 맨 앞으로 이동합니다.
이를 위해 도우미 메서드에서 다음 코드를 사용했습니다.
var path = url.Path;
// Is the file already open?
for(int n=0; n<NSApplication.SharedApplication.Windows.Length; ++n) {
var content = NSApplication.SharedApplication.Windows[n].ContentViewController as ViewController;
if (content != null && path == content.FilePath) {
// Bring window to front
NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
return true;
}
}
속성에 파일 경로를 보관하도록 클래스를 디자인 ViewController
했습니다 Path
. 다음으로, 앱에서 현재 열려 있는 모든 창을 반복합니다. 파일이 이미 창 중 하나에서 열려 있는 경우 다음을 사용하여 다른 모든 창의 맨 앞으로 가져옵니다.
NSApplication.SharedApplication.Windows[n].MakeKeyAndOrderFront(this);
일치하는 항목이 없으면 파일이 로드된 상태로 새 창이 열리고 파일은 [최근 항목 열기] 메뉴에 표시됩니다.
// Get new window
var storyboard = NSStoryboard.FromName ("Main", null);
var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;
// Display
controller.ShowWindow(this);
// Load the text into the window
var viewController = controller.Window.ContentViewController as ViewController;
viewController.Text = File.ReadAllText(path);
viewController.SetLanguageFromPath(path);
viewController.View.Window.SetTitleWithRepresentedFilename (Path.GetFileName(path));
viewController.View.Window.RepresentedUrl = url;
// Add document to the Open Recent menu
NSDocumentController.SharedDocumentController.NoteNewRecentDocumentURL(url);
사용자 지정 창 작업
표준 메뉴 항목에 미리 연결된 기본 제공 첫 번째 응답자 작업과 마찬가지로 새 사용자 지정 작업을 만들고 인터페이스 작성기에서 메뉴 항목에 연결할 수 있습니다.
먼저 앱의 창 컨트롤러 중 하나에서 사용자 지정 작업을 정의합니다. 예시:
[Action("defineKeyword:")]
public void defineKeyword (NSObject sender) {
// Preform some action when the menu is selected
Console.WriteLine ("Request to define keyword");
}
다음으로 Solution Pad에서 앱의 스토리보드 파일을 두 번 클릭하여 Xcode의 인터페이스 작성기에서 편집할 수 있도록 엽니다. 애플리케이션 장면에서 첫 번째 응답자를 선택한 다음, 특성 검사로 전환합니다.
+ 특성 검사기 아래쪽의 단추를 클릭하여 새 사용자 지정 작업을 추가합니다.
창 컨트롤러에서 만든 사용자 지정 작업과 동일한 이름을 지정합니다.
컨트롤을 클릭하고 메뉴 항목에서 애플리케이션 장면 아래의 첫 번째 응답자 로 끌어옵니다. 팝업 목록에서 방금 만든 새 작업을 선택합니다(defineKeyword:
이 예제에서는).
변경 내용을 스토리보드에 저장하고 Mac용 Visual Studio 돌아가 변경 내용을 동기화합니다. 앱을 실행하면 사용자 지정 작업을 연결한 메뉴 항목이 자동으로 활성화/비활성화되고(작업이 열려 있는 창에 따라) 메뉴 항목을 선택하면 작업이 실행됩니다.
메뉴 추가, 편집 및 삭제
이전 섹션에서 보았듯이 Xamarin.Mac 애플리케이션에는 특정 UI 컨트롤이 자동으로 활성화되고 응답하는 기본 메뉴 및 메뉴 항목의 미리 설정된 수가 함께 제공됩니다. 또한 이러한 기본 항목을 사용하도록 설정하고 응답하는 코드를 애플리케이션에 추가하는 방법도 알아보았습니다.
이 섹션에서는 필요하지 않은 메뉴 항목을 제거하고, 메뉴를 다시 구성하고, 새 메뉴, 메뉴 항목 및 작업을 추가하는 것을 살펴봅니다.
Solution Pad에서 Main.storyboard 파일을 두 번 클릭하여 편집용으로 엽니다.
특정 Xamarin.Mac 애플리케이션의 경우 기본 보기 메뉴를 사용하지 않으므로 제거하려고 합니다. 인터페이스 계층 구조에서 기본 메뉴 모음의 일부인 보기 메뉴 항목을 선택합니다.
삭제 또는 백스페이스를 눌러 메뉴를 삭제합니다. 다음으로 서식 메뉴의 모든 항목을 사용하지 않을 것이며 하위 메뉴에서 사용할 항목을 이동하려고 합니다. 인터페이스 계층 구조에서 다음 메뉴 항목을 선택합니다.
현재 있는 하위 메뉴에서 상위 메뉴 아래의 항목을 끕니다.
이제 메뉴는 다음과 같이 표시됩니다.
다음으로 서식 메뉴에서 텍스트 하위 메뉴를 밖으로 끌어서 서식 및 창 메뉴 사이의 기본 메뉴 모음에 배치해 보겠습니다.
서식 메뉴로 돌아가서 글꼴 하위 메뉴 항목을 삭제해 보겠습니다. 다음으로 서식 메뉴를 선택하고 이름을 "글꼴"로 바꿉니다.
다음으로, 선택 시 텍스트 보기의 텍스트에 자동으로 추가되는 미리 정의 구의 사용자 지정 메뉴를 만들어 보겠습니다. 라이브러리 검사기 아래쪽의 검색 상자에 "메뉴"를 입력합니다. 이렇게 하면 모든 메뉴 UI 요소를 더 쉽게 찾고 작업할 수 있습니다.
이제 다음을 수행하여 메뉴를 만들어 보겠습니다.
라이브러리 검사기의 메뉴 항목을 텍스트 및 창 메뉴 사이의 메뉴 모음으로 끌어다 놓습니다.
항목 이름을 "Phrases"로 바꿉니다.
그런 다음 라이브러리 검사기에서 메뉴를 끌어옵니다.
방금 만든 새 메뉴 항목에서 메뉴를 삭제하고 이름을 "구"로 변경합니다.
이제 세 가지 기본 메뉴 항목 "주소", "날짜" 및 "인사말"의 이름을 바꾸겠습니다.
라이브러리 검사기에서 메뉴 항목을 끌어서 "서명"이라고 호출하여 네 번째 메뉴 항목을 추가해 보겠습니다.
메뉴 모음에 변경 내용을 저장합니다.
이제 새 메뉴 항목이 C# 코드에 노출되도록 사용자 지정 작업 집합을 만들어 보겠습니다. Xcode에서 도우미 보기로 전환해 보겠습니다.
다음을 수행해 보겠습니다.
주소 메뉴 항목에서 AppDelegate.h 파일로 컨트롤 끌기
커넥트ion 형식을 작업으로 전환합니다.
"phraseAddress"의 이름을 입력하고 커넥트 단추를 눌러 새 작업을 만듭니다.
날짜, 인사말 및 서명 메뉴 항목에 대해 위의 단계를 반복합니다.
메뉴 모음에 변경 내용을 저장합니다.
다음으로 코드에서 콘텐츠를 조정할 수 있도록 텍스트 보기에 대한 콘센트를 만들어야 합니다. 보조 편집기에서 ViewController.h 파일을 선택하고 다음과 같은 documentText
새 콘센트를 만듭니다.
Mac용 Visual Studio 돌아가서 Xcode의 변경 내용을 동기화합니다. 그런 다음, ViewController.cs 파일을 편집하고 다음과 같이 표시합니다.
using System;
using AppKit;
using Foundation;
namespace MacMenus
{
public partial class ViewController : NSViewController
{
#region Application Access
public static AppDelegate App {
get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
}
#endregion
#region Computed Properties
public override NSObject RepresentedObject {
get {
return base.RepresentedObject;
}
set {
base.RepresentedObject = value;
// Update the view, if already loaded.
}
}
public string Text {
get { return documentText.Value; }
set { documentText.Value = value; }
}
#endregion
#region Constructors
public ViewController (IntPtr handle) : base (handle)
{
}
#endregion
#region Override Methods
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Do any additional setup after loading the view.
}
public override void ViewWillAppear ()
{
base.ViewWillAppear ();
App.textEditor = this;
}
public override void ViewWillDisappear ()
{
base.ViewDidDisappear ();
App.textEditor = null;
}
#endregion
}
}
이렇게 하면 클래스 외부에 있는 텍스트 보기의 텍스트가 ViewController
노출되고 창이 포커스를 얻거나 잃을 때 앱 대리자에게 알립니다. 이제 AppDelegate.cs 파일을 편집하고 다음과 같이 표시합니다.
using AppKit;
using Foundation;
using System;
namespace MacMenus
{
[Register ("AppDelegate")]
public partial class AppDelegate : NSApplicationDelegate
{
#region Computed Properties
public ViewController textEditor { get; set;} = null;
#endregion
#region Constructors
public AppDelegate ()
{
}
#endregion
#region Override Methods
public override void DidFinishLaunching (NSNotification notification)
{
// Insert code here to initialize your application
}
public override void WillTerminate (NSNotification notification)
{
// Insert code here to tear down your application
}
#endregion
#region Custom actions
[Export ("openDocument:")]
void OpenDialog (NSObject sender)
{
var dlg = NSOpenPanel.OpenPanel;
dlg.CanChooseFiles = false;
dlg.CanChooseDirectories = true;
if (dlg.RunModal () == 1) {
var alert = new NSAlert () {
AlertStyle = NSAlertStyle.Informational,
InformativeText = "At this point we should do something with the folder that the user just selected in the Open File Dialog box...",
MessageText = "Folder Selected"
};
alert.RunModal ();
}
}
partial void phrasesAddress (Foundation.NSObject sender) {
textEditor.Text += "Xamarin HQ\n394 Pacific Ave, 4th Floor\nSan Francisco CA 94111\n\n";
}
partial void phrasesDate (Foundation.NSObject sender) {
textEditor.Text += DateTime.Now.ToString("D");
}
partial void phrasesGreeting (Foundation.NSObject sender) {
textEditor.Text += "Dear Sirs,\n\n";
}
partial void phrasesSignature (Foundation.NSObject sender) {
textEditor.Text += "Sincerely,\n\nKevin Mullins\nXamarin,Inc.\n";
}
#endregion
}
}
여기서는 Interface Builder에서 AppDelegate
정의한 작업 및 콘센트를 사용할 수 있도록 부분 클래스를 만들었습니다. 또한 현재 포커스가 textEditor
있는 창을 추적하기 위해 노출합니다.
다음 메서드는 사용자 지정 메뉴 및 메뉴 항목을 처리하는 데 사용됩니다.
partial void phrasesAddress (Foundation.NSObject sender) {
if (textEditor == null) return;
textEditor.Text += "Xamarin HQ\n394 Pacific Ave, 4th Floor\nSan Francisco CA 94111\n\n";
}
partial void phrasesDate (Foundation.NSObject sender) {
if (textEditor == null) return;
textEditor.Text += DateTime.Now.ToString("D");
}
partial void phrasesGreeting (Foundation.NSObject sender) {
if (textEditor == null) return;
textEditor.Text += "Dear Sirs,\n\n";
}
partial void phrasesSignature (Foundation.NSObject sender) {
if (textEditor == null) return;
textEditor.Text += "Sincerely,\n\nKevin Mullins\nXamarin,Inc.\n";
}
이제 애플리케이션을 실행하면 구 메뉴의 모든 항목이 활성화되고 선택 시 텍스트 보기에 제공 구가 추가됩니다.
이제 애플리케이션 메뉴 모음을 사용하는 기본 사항이 있으므로 사용자 지정 상황에 맞는 메뉴를 만들어 보겠습니다.
코드에서 메뉴 만들기
Xcode의 인터페이스 작성기를 사용하여 메뉴 및 메뉴 항목을 만드는 것 외에도 Xamarin.Mac 앱이 코드에서 메뉴, 하위 메뉴 또는 메뉴 항목을 만들거나 수정하거나 제거해야 하는 경우가 있을 수 있습니다.
다음 예제에서는 즉시 동적으로 생성될 메뉴 항목 및 하위 메뉴에 대한 정보를 보관하는 클래스가 만들어집니다.
using System;
using System.Collections.Generic;
using Foundation;
using AppKit;
namespace AppKit.TextKit.Formatter
{
public class LanguageFormatCommand : NSObject
{
#region Computed Properties
public string Title { get; set; } = "";
public string Prefix { get; set; } = "";
public string Postfix { get; set; } = "";
public List<LanguageFormatCommand> SubCommands { get; set; } = new List<LanguageFormatCommand>();
#endregion
#region Constructors
public LanguageFormatCommand () {
}
public LanguageFormatCommand (string title)
{
// Initialize
this.Title = title;
}
public LanguageFormatCommand (string title, string prefix)
{
// Initialize
this.Title = title;
this.Prefix = prefix;
}
public LanguageFormatCommand (string title, string prefix, string postfix)
{
// Initialize
this.Title = title;
this.Prefix = prefix;
this.Postfix = postfix;
}
#endregion
}
}
메뉴 및 항목 추가
이 클래스가 정의된 경우 다음 루틴은 개체 컬렉션을 LanguageFormatCommand
구문 분석하고 전달된 기존 메뉴(인터페이스 작성기에서 만든)의 맨 아래에 추가하여 새 메뉴 및 메뉴 항목을 재귀적으로 작성합니다.
private void AssembleMenu(NSMenu menu, List<LanguageFormatCommand> commands) {
NSMenuItem menuItem;
// Add any formatting commands to the Formatting menu
foreach (LanguageFormatCommand command in commands) {
// Add separator or item?
if (command.Title == "") {
menuItem = NSMenuItem.SeparatorItem;
} else {
menuItem = new NSMenuItem (command.Title);
// Submenu?
if (command.SubCommands.Count > 0) {
// Yes, populate submenu
menuItem.Submenu = new NSMenu (command.Title);
AssembleMenu (menuItem.Submenu, command.SubCommands);
} else {
// No, add normal menu item
menuItem.Activated += (sender, e) => {
// Apply the command on the selected text
TextEditor.PerformFormattingCommand (command);
};
}
}
menu.AddItem (menuItem);
}
}
빈 Title
속성이 있는 개체 LanguageFormatCommand
의 경우 이 루틴은 메뉴 섹션 사이에 구분 기호 메뉴 항목(얇은 회색 선)을 만듭니다.
menuItem = NSMenuItem.SeparatorItem;
제목이 제공되면 해당 제목이 있는 새 메뉴 항목이 만들어집니다.
menuItem = new NSMenuItem (command.Title);
개체에 LanguageFormatCommand
자식 LanguageFormatCommand
개체가 포함된 경우 하위 메뉴가 만들어지고 AssembleMenu
해당 메뉴를 빌드하기 위해 메서드가 재귀적으로 호출됩니다.
menuItem.Submenu = new NSMenu (command.Title);
AssembleMenu (menuItem.Submenu, command.SubCommands);
하위 메뉴가 없는 새 메뉴 항목의 경우 사용자가 선택하는 메뉴 항목을 처리하기 위해 코드가 추가됩니다.
menuItem.Activated += (sender, e) => {
// Do something when the menu item is selected
...
};
메뉴 만들기 테스트
다음 개체 컬렉션을 LanguageFormatCommand
만든 경우 위의 모든 코드를 사용합니다.
// Define formatting commands
FormattingCommands.Add(new LanguageFormatCommand("Strong","**","**"));
FormattingCommands.Add(new LanguageFormatCommand("Emphasize","_","_"));
FormattingCommands.Add(new LanguageFormatCommand("Inline Code","`","`"));
FormattingCommands.Add(new LanguageFormatCommand("Code Block","```\n","\n```"));
FormattingCommands.Add(new LanguageFormatCommand("Comment","<!--","-->"));
FormattingCommands.Add (new LanguageFormatCommand ());
FormattingCommands.Add(new LanguageFormatCommand("Unordered List","* "));
FormattingCommands.Add(new LanguageFormatCommand("Ordered List","1. "));
FormattingCommands.Add(new LanguageFormatCommand("Block Quote","> "));
FormattingCommands.Add (new LanguageFormatCommand ());
var Headings = new LanguageFormatCommand ("Headings");
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 1","# "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 2","## "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 3","### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 4","#### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 5","##### "));
Headings.SubCommands.Add(new LanguageFormatCommand("Heading 6","###### "));
FormattingCommands.Add (Headings);
FormattingCommands.Add(new LanguageFormatCommand ());
FormattingCommands.Add(new LanguageFormatCommand("Link","[","]()"));
FormattingCommands.Add(new LanguageFormatCommand("Image","![](",")"));
FormattingCommands.Add(new LanguageFormatCommand("Image Link","[![](",")](LinkImageHere)"));
그리고 해당 컬렉션이 함수에 AssembleMenu
전달되고( 서식 메뉴가 기본으로 설정됨) 다음과 같은 동적 메뉴 및 메뉴 항목이 생성됩니다.
메뉴 및 항목 제거
앱의 사용자 인터페이스에서 메뉴 또는 메뉴 항목을 제거해야 하는 경우 제거할 항목의 NSMenu
인덱스(0부터 시작)를 제공하여 클래스의 메서드를 사용할 RemoveItemAt
수 있습니다.
예를 들어 위의 루틴에서 만든 메뉴 및 메뉴 항목을 제거하려면 다음 코드를 사용할 수 있습니다.
public void UnpopulateFormattingMenu(NSMenu menu) {
// Remove any additional items
for (int n = (int)menu.Count - 1; n > 4; --n) {
menu.RemoveItemAt (n);
}
}
위의 코드의 경우 처음 네 개의 메뉴 항목은 Xcode의 인터페이스 작성기에서 만들어지고 앱에서 사용할 수 있는 어웨이에서 만들어지므로 동적으로 제거되지 않습니다.
상황에 맞는 메뉴
상황에 맞는 메뉴는 사용자가 창에서 항목을 마우스 오른쪽 단추로 클릭하거나 컨트롤을 클릭할 때 표시됩니다. 기본적으로 macOS에 기본 제공되는 여러 UI 요소에는 이미 상황에 맞는 메뉴(예: 텍스트 보기)가 연결되어 있습니다. 그러나 창에 추가한 UI 요소에 대한 사용자 지정 상황에 맞는 메뉴를 만들려는 경우가 있을 수 있습니다.
Xcode에서 Main.storyboard 파일을 편집하고, 디자인에 창 창을 추가하고, 해당 클래스를 ID 검사기에서 "NSPanel"로 설정하고, 창 메뉴에 새 도우미 항목을 추가하고, Segue 표시를 사용하여 새 창에 연결해 보겠습니다.
다음을 수행해 보겠습니다.
라이브러리 검사기에서 패널 창으로 레이블을 끌어와 텍스트를 "속성"으로 설정합니다.
그런 다음 라이브러리 검사기의 메뉴를 보기 계층 구조의 보기 컨트롤러로 끌어다 놓고 문서, 텍스트 및 글꼴의 세 가지 기본 메뉴 항목 이름을 바꿉니다.
이제 속성 레이블에서 메뉴로 컨트롤 끌기:
팝업 대화 상자에서 메뉴를 선택합니다.
ID 검사기에서 뷰 컨트롤러의 클래스를 "PanelViewController"로 설정합니다.
동기화할 Mac용 Visual Studio 다시 전환한 다음 Interface Builder로 돌아갑니다.
도우미 편집기로 전환하고 PanelViewController.h 파일을 선택합니다.
다음과 같은 문서 메뉴 항목에 대한 작업을 만듭니다
propertyDocument
.다시 기본 메뉴 항목에 대한 작업 만들기를 반복합니다.
마지막으로 다음과 같은 속성 레이블에 대한 콘센트를 만듭니다
propertyLabel
.변경 내용을 저장하고 Mac용 Visual Studio 돌아가 Xcode와 동기화합니다.
PanelViewController.cs 파일을 편집하고 다음 코드를 추가합니다.
partial void propertyDocument (Foundation.NSObject sender) {
propertyLabel.StringValue = "Document";
}
partial void propertyFont (Foundation.NSObject sender) {
propertyLabel.StringValue = "Font";
}
partial void propertyText (Foundation.NSObject sender) {
propertyLabel.StringValue = "Text";
}
이제 애플리케이션을 실행하고 패널에서 속성 레이블을 마우스 오른쪽 단추로 클릭하면 사용자 지정 상황에 맞는 메뉴가 표시됩니다. 메뉴에서 항목을 선택하고 선택하면 레이블의 값이 변경됩니다.
다음으로 상태 모음 메뉴를 만드는 방법에 대해 살펴보겠습니다.
상태 표시줄 메뉴
상태 표시줄 메뉴에는 애플리케이션의 상태를 반영하는 메뉴 또는 이미지와 같이 사용자에게 상호 작용 또는 피드백을 제공하는 상태 메뉴 항목의 컬렉션이 표시됩니다. 애플리케이션이 백그라운드에서 실행 중인 경우에도 애플리케이션의 상태 표시줄 메뉴가 활성화되고 활성화됩니다. 시스템 전체 상태 막대는 애플리케이션 메뉴 모음의 오른쪽에 있으며 현재 macOS에서 사용할 수 있는 유일한 상태 표시줄입니다.
AppDelegate.cs 파일을 편집하고 메서드를 DidFinishLaunching
다음과 같이 만들어 보겠습니다.
public override void DidFinishLaunching (NSNotification notification)
{
// Create a status bar menu
NSStatusBar statusBar = NSStatusBar.SystemStatusBar;
var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable);
item.Title = "Text";
item.HighlightMode = true;
item.Menu = new NSMenu ("Text");
var address = new NSMenuItem ("Address");
address.Activated += (sender, e) => {
PhraseAddress(address);
};
item.Menu.AddItem (address);
var date = new NSMenuItem ("Date");
date.Activated += (sender, e) => {
PhraseDate(date);
};
item.Menu.AddItem (date);
var greeting = new NSMenuItem ("Greeting");
greeting.Activated += (sender, e) => {
PhraseGreeting(greeting);
};
item.Menu.AddItem (greeting);
var signature = new NSMenuItem ("Signature");
signature.Activated += (sender, e) => {
PhraseSignature(signature);
};
item.Menu.AddItem (signature);
}
NSStatusBar statusBar = NSStatusBar.SystemStatusBar;
에서는 시스템 차원의 상태 막대에 액세스할 수 있습니다. var item = statusBar.CreateStatusItem (NSStatusItemLength.Variable);
는 새 상태 막대 항목을 만듭니다. 여기에서 메뉴와 여러 메뉴 항목을 만들고 방금 만든 상태 막대 항목에 메뉴를 연결합니다.
애플리케이션을 실행하면 새 상태 막대 항목이 표시됩니다. 메뉴에서 항목을 선택하면 텍스트 보기의 텍스트가 변경됩니다.
다음으로 사용자 지정 도크 메뉴 항목을 만드는 방법에 대해 살펴보겠습니다.
사용자 지정 도크 메뉴
사용자가 도크에서 애플리케이션의 아이콘을 마우스 오른쪽 단추로 클릭하거나 컨트롤을 클릭하면 Mac 애플리케이션에 대한 도크 메뉴가 나타납니다.
다음을 수행하여 애플리케이션에 대한 사용자 지정 도크 메뉴를 만들어 보겠습니다.
Mac용 Visual Studio 애플리케이션의 프로젝트를 마우스 오른쪽 단추로 클릭하고 새 파일 추가>...를 선택합니다. 새 파일 대화 상자에서 Xamarin.Mac 빈 인터페이스 정의를 선택하고 이름에 "DockMenu"를 사용하고 새로 만들기 단추를 클릭하여 새 DockMenu.xib 파일을 만듭니다.>
Solution Pad에서 DockMenu.xib 파일을 두 번 클릭하여 Xcode에서 편집할 수 있도록 엽니다. 주소, 날짜, 인사말 및 서명 항목으로 새 메뉴를 만듭니다.
다음으로, 위의 메뉴 추가, 편집 및 삭제 섹션에서 사용자 지정 메뉴 에 대해 만든 기존 작업에 새 메뉴 항목을 연결해 보겠습니다. 커넥트ion Inspector로 전환하고 인터페이스 계층 구조에서 첫 번째 응답자를 선택합니다. 아래로 스크롤하여 작업을 찾습니다
phraseAddress:
. 해당 작업의 원에서 주소 메뉴 항목으로 선을 끕니다.해당 작업에 연결하는 다른 모든 메뉴 항목에 대해 반복합니다.
다음으로 인터페이스 계층 구조에서 애플리케이션을 선택합니다. 커넥트 검사기에서 콘센트의 원
dockMenu
에서 방금 만든 메뉴로 선을 끌어옵니다.변경 내용을 저장하고 Mac용 Visual Studio 다시 전환하여 Xcode와 동기화합니다.
Info.plist 파일을 두 번 클릭하여 편집용으로 엽니다.
화면 아래쪽의 원본 탭을 클릭합니다.
새 항목 추가를 클릭하고 녹색 더하기 단추를 클릭하고 속성 이름을 "AppleDockMenu"로 설정하고 값을 "DockMenu"(확장명 없는 새 .xib 파일의 이름)로 설정합니다.
이제 애플리케이션을 실행하고 Dock에서 해당 아이콘을 마우스 오른쪽 단추로 클릭하면 새 메뉴 항목이 표시됩니다.
메뉴에서 사용자 지정 항목 중 하나를 선택하면 텍스트 보기의 텍스트가 수정됩니다.
팝업 단추 및 풀다운 목록
팝업 단추는 선택한 항목을 표시하고 사용자가 클릭할 때 선택할 수 있는 옵션 목록을 표시합니다. 풀다운 목록은 일반적으로 현재 작업의 컨텍스트와 관련된 명령을 선택하는 데 사용되는 팝업 단추의 유형입니다. 둘 다 창의 아무 곳에나 나타날 수 있습니다.
다음을 수행하여 애플리케이션에 대한 사용자 지정 팝업 단추를 만들어 보겠습니다.
Xcode에서 Main.storyboard 파일을 편집하고 라이브러리 검사기의 팝업 단추를 상황에 맞는 메뉴 섹션에서 만든 패널 창으로 끕니다.
새 메뉴 항목을 추가하고 팝업의 항목 제목을 주소, 날짜, 인사말 및 서명으로 설정합니다.
다음으로, 위의 메뉴 추가, 편집 및 삭제 섹션에서 사용자 지정 메뉴 에 대해 만든 기존 작업에 새 메뉴 항목을 연결해 보겠습니다. 커넥트ion Inspector로 전환하고 인터페이스 계층 구조에서 첫 번째 응답자를 선택합니다. 아래로 스크롤하여 작업을 찾습니다
phraseAddress:
. 해당 작업의 원에서 주소 메뉴 항목으로 선을 끕니다.해당 작업에 연결하는 다른 모든 메뉴 항목에 대해 반복합니다.
변경 내용을 저장하고 Mac용 Visual Studio 다시 전환하여 Xcode와 동기화합니다.
이제 애플리케이션을 실행하고 팝업에서 항목을 선택하면 텍스트 보기의 텍스트가 변경됩니다.
팝업 단추와 똑같은 방식으로 풀다운 목록을 만들고 작업할 수 있습니다. 기존 작업에 연결하는 대신 상황에 맞는 메뉴 섹션의 상황에 맞는 메뉴에 대해 수행한 것처럼 고유한 사용자 지정 작업을 만들 수 있습니다 .
요약
이 문서에서는 Xamarin.Mac 애플리케이션의 메뉴 및 메뉴 항목 작업에 대해 자세히 살펴보았습니다. 먼저 애플리케이션의 메뉴 모음을 살펴본 다음 상황에 맞는 메뉴를 만드는 것을 살펴본 다음 상태 바 메뉴와 사용자 지정 도크 메뉴를 검사했습니다. 마지막으로 팝업 메뉴와 풀다운 목록을 설명했습니다.