private bool AddClipboardItems(TextEditor textEditor, bool userInitiated) { MenuItem menuItem; menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Cut); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Cut; this.Items.Add(menuItem); menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Copy); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Copy; this.Items.Add(menuItem); // create a special menu item for paste which only works for user initiated paste // within the confines of partial trust this cannot be done programmatically if (userInitiated == false) { SecurityHelper.DemandAllClipboardPermission(); } menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Paste); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Paste; this.Items.Add(menuItem); return(true); }
private bool AddReconversionItems(TextEditor textEditor) { MenuItem menuItem; TextStore textStore = textEditor.TextStore; if (textStore == null) { GC.SuppressFinalize(this); return(false); } ReleaseCandidateList(null); _candidateList = new SecurityCriticalDataClass <UnsafeNativeMethods.ITfCandidateList>(textStore.GetReconversionCandidateList()); if (CandidateList == null) { GC.SuppressFinalize(this); return(false); } int count = 0; CandidateList.GetCandidateNum(out count); if (count > 0) { // Like Winword, we show the first 5 candidates in the context menu. int i; for (i = 0; i < 5 && i < count; i++) { string suggestion; UnsafeNativeMethods.ITfCandidateString candString; CandidateList.GetCandidate(i, out candString); candString.GetString(out suggestion); menuItem = new ReconversionMenuItem(this, i); menuItem.Header = suggestion; menuItem.InputGestureText = GetMenuItemDescription(suggestion); this.Items.Add(menuItem); Marshal.ReleaseComObject(candString); } } // Like Winword, we show "More" menu to open TIP's candidate list if there are more // than 5 candidates. if (count > 5) { menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_More); menuItem.Command = ApplicationCommands.CorrectionList; this.Items.Add(menuItem); menuItem.CommandTarget = textEditor.UiScope; } return((count > 0) ? true : false); }
// Appends spell check related items. // Returns false if no items are added. private bool AddSpellerItems(TextEditor textEditor) { SpellingError spellingError; MenuItem menuItem; spellingError = textEditor.GetSpellingErrorAtSelection(); if (spellingError == null) { return(false); } bool addedSuggestion = false; foreach (string suggestion in spellingError.Suggestions) { menuItem = new EditorMenuItem(); TextBlock text = new TextBlock(); text.FontWeight = FontWeights.Bold; text.Text = suggestion; menuItem.Header = text; menuItem.Command = EditingCommands.CorrectSpellingError; menuItem.CommandParameter = suggestion; this.Items.Add(menuItem); menuItem.CommandTarget = textEditor.UiScope; addedSuggestion = true; } if (!addedSuggestion) { menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_NoSpellingSuggestions); menuItem.IsEnabled = false; this.Items.Add(menuItem); } AddSeparator(); menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_IgnoreAll); menuItem.Command = EditingCommands.IgnoreSpellingError; this.Items.Add(menuItem); menuItem.CommandTarget = textEditor.UiScope; return(true); }
// Appends clipboard related items. // Returns false if no items are added. private bool AddClipboardItems(TextEditor textEditor) { MenuItem menuItem; menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Cut); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Cut; this.Items.Add(menuItem); menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Copy); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Copy; this.Items.Add(menuItem); menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Paste); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Paste; this.Items.Add(menuItem); return(true); }
private void load(OsuColour colours, OsuConfigManager config) { var loadableBeatmap = Beatmap.Value; if (loadableBeatmap is DummyWorkingBeatmap) { isNewBeatmap = true; loadableBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value); // required so we can get the track length in EditorClock. // this is safe as nothing has yet got a reference to this new beatmap. loadableBeatmap.LoadTrack(); // this is a bit haphazard, but guards against setting the lease Beatmap bindable if // the editor has already been exited. if (!ValidForPush) { return; } } try { playableBeatmap = loadableBeatmap.GetPlayableBeatmap(loadableBeatmap.BeatmapInfo.Ruleset); // clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages. // eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases. playableBeatmap.ControlPointInfo = playableBeatmap.ControlPointInfo.DeepClone(); } catch (Exception e) { Logger.Error(e, "Could not load beatmap successfully!"); // couldn't load, hard abort! this.Exit(); return; } // Todo: should probably be done at a DrawableRuleset level to share logic with Player. clock = new EditorClock(playableBeatmap, beatDivisor) { IsCoupled = false }; clock.ChangeSource(loadableBeatmap.Track); dependencies.CacheAs(clock); AddInternal(clock); clock.SeekingOrStopped.BindValueChanged(_ => updateSampleDisabledState()); // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, loadableBeatmap.GetSkin(), loadableBeatmap.BeatmapInfo)); dependencies.CacheAs(editorBeatmap); canSave = editorBeatmap.BeatmapInfo.Ruleset.CreateInstance() is ILegacyRuleset; if (canSave) { changeHandler = new EditorChangeHandler(editorBeatmap); dependencies.CacheAs <IEditorChangeHandler>(changeHandler); } beatDivisor.Value = editorBeatmap.BeatmapInfo.BeatDivisor; beatDivisor.BindValueChanged(divisor => editorBeatmap.BeatmapInfo.BeatDivisor = divisor.NewValue); updateLastSavedHash(); Schedule(() => { // we need to avoid changing the beatmap from an asynchronous load thread. it can potentially cause weirdness including crashes. // this assumes that nothing during the rest of this load() method is accessing Beatmap.Value (loadableBeatmap should be preferred). // generally this is quite safe, as the actual load of editor content comes after menuBar.Mode.ValueChanged is fired in its own LoadComplete. Beatmap.Value = loadableBeatmap; }); OsuMenuItem undoMenuItem; OsuMenuItem redoMenuItem; AddInternal(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, Children = new[] { new Container { Name = "Screen container", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 40, Bottom = 60 }, Child = screenContainer = new Container <EditorScreen> { RelativeSizeAxes = Axes.Both, Masking = true } }, new Container { Name = "Top bar", RelativeSizeAxes = Axes.X, Height = 40, Children = new Drawable[] { new EditorMenuBar { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, Items = new[] { new MenuItem("File") { Items = createFileMenuItems() }, new MenuItem(CommonStrings.ButtonsEdit) { Items = new[] { undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, Undo), redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, Redo), new EditorMenuItemSpacer(), cutMenuItem = new EditorMenuItem("Cut", MenuItemType.Standard, Cut), copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy), pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste), } }, new MenuItem("View") { Items = new MenuItem[] { new WaveformOpacityMenuItem(config.GetBindable <float>(OsuSetting.EditorWaveformOpacity)), new HitAnimationsMenuItem(config.GetBindable <bool>(OsuSetting.EditorHitAnimations)) } } } }, new ScreenSelectionTabControl { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, X = -15, Current = Mode, }, }, }, new Container { Name = "Bottom bar", Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, Height = 60, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = colours.Gray2 }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Vertical = 5, Horizontal = 10 }, Child = new GridContainer { RelativeSizeAxes = Axes.Both, ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, 220), new Dimension(), new Dimension(GridSizeMode.Absolute, 220), new Dimension(GridSizeMode.Absolute, 120), }, Content = new[] { new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 10 }, Child = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, }, new SummaryTimeline { RelativeSizeAxes = Axes.Both, }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 10 }, Child = new PlaybackControl { RelativeSizeAxes = Axes.Both }, }, testGameplayButton = new TestGameplayButton { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 10 }, Size = new Vector2(1), Action = testGameplay } }, } }, } } }, } }); changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true); changeHandler?.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true); }
private void load(OsuColour colours, GameHost host) { beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor; beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue); // Todo: should probably be done at a DrawableRuleset level to share logic with Player. clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false }; UpdateClockSource(); dependencies.CacheAs(clock); dependencies.CacheAs <ISamplePlaybackDisabler>(clock); AddInternal(clock); // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); if (Beatmap.Value is DummyWorkingBeatmap) { isNewBeatmap = true; Beatmap.Value = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value); } try { playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); } catch (Exception e) { Logger.Error(e, "Could not load beatmap successfully!"); // couldn't load, hard abort! this.Exit(); return; } AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, Beatmap.Value.Skin)); dependencies.CacheAs(editorBeatmap); changeHandler = new EditorChangeHandler(editorBeatmap); dependencies.CacheAs <IEditorChangeHandler>(changeHandler); updateLastSavedHash(); OsuMenuItem undoMenuItem; OsuMenuItem redoMenuItem; EditorMenuItem cutMenuItem; EditorMenuItem copyMenuItem; EditorMenuItem pasteMenuItem; var fileMenuItems = new List <MenuItem> { new EditorMenuItem("Save", MenuItemType.Standard, Save) }; if (RuntimeInfo.IsDesktop) { fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); } fileMenuItems.Add(new EditorMenuItemSpacer()); fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); AddInternal(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, Children = new[] { new Container { Name = "Screen container", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 40, Bottom = 60 }, Child = screenContainer = new Container <EditorScreen> { RelativeSizeAxes = Axes.Both, Masking = true } }, new Container { Name = "Top bar", RelativeSizeAxes = Axes.X, Height = 40, Child = menuBar = new EditorMenuBar { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, Mode = { Value = isNewBeatmap ? EditorScreenMode.SongSetup : EditorScreenMode.Compose }, Items = new[] { new MenuItem("File") { Items = fileMenuItems }, new MenuItem("Edit") { Items = new[] { undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, Undo), redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, Redo), new EditorMenuItemSpacer(), cutMenuItem = new EditorMenuItem("Cut", MenuItemType.Standard, Cut), copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy), pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste), } } } } }, new Container { Name = "Bottom bar", Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, Height = 60, Children = new Drawable[] { bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Vertical = 5, Horizontal = 10 }, Child = new GridContainer { RelativeSizeAxes = Axes.Both, ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, 220), new Dimension(), new Dimension(GridSizeMode.Absolute, 220) }, Content = new[] { new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 10 }, Child = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, }, new SummaryTimeline { RelativeSizeAxes = Axes.Both, }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 10 }, Child = new PlaybackControl { RelativeSizeAxes = Axes.Both }, } }, } }, } } }, } }); changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true); changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true); editorBeatmap.SelectedHitObjects.BindCollectionChanged((_, __) => { var hasObjects = editorBeatmap.SelectedHitObjects.Count > 0; cutMenuItem.Action.Disabled = !hasObjects; copyMenuItem.Action.Disabled = !hasObjects; }, true); clipboard.BindValueChanged(content => pasteMenuItem.Action.Disabled = string.IsNullOrEmpty(content.NewValue)); menuBar.Mode.ValueChanged += onModeChanged; bottomBackground.Colour = colours.Gray2; }
private void load(OsuColour colours, GameHost host) { beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor; beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue); // Todo: should probably be done at a DrawableRuleset level to share logic with Player. var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false }; clock.ChangeSource(sourceClock); dependencies.CacheAs <IFrameBasedClock>(clock); dependencies.CacheAs <IAdjustableClock>(clock); // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); try { playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); } catch (Exception e) { Logger.Error(e, "Could not load beatmap successfully!"); // couldn't load, hard abort! this.Exit(); return; } AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap)); dependencies.CacheAs(editorBeatmap); changeHandler = new EditorChangeHandler(editorBeatmap); dependencies.CacheAs <IEditorChangeHandler>(changeHandler); EditorMenuBar menuBar; OsuMenuItem undoMenuItem; OsuMenuItem redoMenuItem; var fileMenuItems = new List <MenuItem> { new EditorMenuItem("Save", MenuItemType.Standard, saveBeatmap) }; if (RuntimeInfo.IsDesktop) { fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); } fileMenuItems.Add(new EditorMenuItemSpacer()); fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); AddInternal(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, Children = new[] { new Container { Name = "Screen container", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 40, Bottom = 60 }, Child = screenContainer = new Container { RelativeSizeAxes = Axes.Both, Masking = true } }, new Container { Name = "Top bar", RelativeSizeAxes = Axes.X, Height = 40, Child = menuBar = new EditorMenuBar { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, Items = new[] { new MenuItem("File") { Items = fileMenuItems }, new MenuItem("Edit") { Items = new[] { undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, undo), redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, redo) } } } } }, new Container { Name = "Bottom bar", Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, Height = 60, Children = new Drawable[] { bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Vertical = 5, Horizontal = 10 }, Child = new GridContainer { RelativeSizeAxes = Axes.Both, ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, 220), new Dimension(), new Dimension(GridSizeMode.Absolute, 220) }, Content = new[] { new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 10 }, Child = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, }, new SummaryTimeline { RelativeSizeAxes = Axes.Both, }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 10 }, Child = new PlaybackControl { RelativeSizeAxes = Axes.Both }, } }, } }, } } }, } }); changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true); changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true); menuBar.Mode.ValueChanged += onModeChanged; bottomBackground.Colour = colours.Gray2; }
private bool AddClipboardItems(TextEditor textEditor, bool userInitiated) { MenuItem menuItem; menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Cut); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Cut; this.Items.Add(menuItem); menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Copy); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Copy; this.Items.Add(menuItem); // create a special menu item for paste which only works for user initiated paste // within the confines of partial trust this cannot be done programmatically if (userInitiated == false) { SecurityHelper.DemandAllClipboardPermission(); } menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_Paste); menuItem.CommandTarget = textEditor.UiScope; menuItem.Command = ApplicationCommands.Paste; this.Items.Add(menuItem); return true; }
private bool AddReconversionItems(TextEditor textEditor) { MenuItem menuItem; TextStore textStore = textEditor.TextStore; if (textStore == null) { GC.SuppressFinalize(this); return false; } ReleaseCandidateList(null); _candidateList = new SecurityCriticalDataClass<UnsafeNativeMethods.ITfCandidateList>(textStore.GetReconversionCandidateList()); if (CandidateList == null) { GC.SuppressFinalize(this); return false; } int count = 0; CandidateList.GetCandidateNum(out count); if (count > 0) { // Like Winword, we show the first 5 candidates in the context menu. int i; for (i = 0; i < 5 && i < count; i++) { string suggestion; UnsafeNativeMethods.ITfCandidateString candString; CandidateList.GetCandidate(i, out candString); candString.GetString(out suggestion); menuItem = new ReconversionMenuItem(this, i); menuItem.Header = suggestion; menuItem.InputGestureText = GetMenuItemDescription(suggestion); this.Items.Add(menuItem); Marshal.ReleaseComObject(candString); } } // Like Winword, we show "More" menu to open TIP's candidate list if there are more // than 5 candidates. if (count > 5) { menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_More); menuItem.Command = ApplicationCommands.CorrectionList; this.Items.Add(menuItem); menuItem.CommandTarget = textEditor.UiScope; } return (count > 0) ? true : false; }
// Appends spell check related items. // Returns false if no items are added. private bool AddSpellerItems(TextEditor textEditor) { SpellingError spellingError; MenuItem menuItem; spellingError = textEditor.GetSpellingErrorAtSelection(); if (spellingError == null) return false; bool addedSuggestion = false; foreach (string suggestion in spellingError.Suggestions) { menuItem = new EditorMenuItem(); TextBlock text = new TextBlock(); text.FontWeight = FontWeights.Bold; text.Text = suggestion; menuItem.Header = text; menuItem.Command = EditingCommands.CorrectSpellingError; menuItem.CommandParameter = suggestion; this.Items.Add(menuItem); menuItem.CommandTarget = textEditor.UiScope; addedSuggestion = true; } if (!addedSuggestion) { menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_NoSpellingSuggestions); menuItem.IsEnabled = false; this.Items.Add(menuItem); } AddSeparator(); menuItem = new EditorMenuItem(); menuItem.Header = SR.Get(SRID.TextBox_ContextMenu_IgnoreAll); menuItem.Command = EditingCommands.IgnoreSpellingError; this.Items.Add(menuItem); menuItem.CommandTarget = textEditor.UiScope; return true; }
private void load(OsuColour colours, GameHost host, OsuConfigManager config) { if (Beatmap.Value is DummyWorkingBeatmap) { isNewBeatmap = true; var newBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value); // this is a bit haphazard, but guards against setting the lease Beatmap bindable if // the editor has already been exited. if (!ValidForPush) { return; } // this probably shouldn't be set in the asynchronous load method, but everything following relies on it. Beatmap.Value = newBeatmap; } beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor; beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue); // Todo: should probably be done at a DrawableRuleset level to share logic with Player. clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false }; UpdateClockSource(); dependencies.CacheAs(clock); AddInternal(clock); clock.SeekingOrStopped.BindValueChanged(_ => updateSampleDisabledState()); // todo: remove caching of this and consume via editorBeatmap? dependencies.Cache(beatDivisor); try { playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset); // clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages. // eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases. playableBeatmap.ControlPointInfo = playableBeatmap.ControlPointInfo.CreateCopy(); } catch (Exception e) { Logger.Error(e, "Could not load beatmap successfully!"); // couldn't load, hard abort! this.Exit(); return; } AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, Beatmap.Value.Skin)); dependencies.CacheAs(editorBeatmap); changeHandler = new EditorChangeHandler(editorBeatmap); dependencies.CacheAs <IEditorChangeHandler>(changeHandler); updateLastSavedHash(); OsuMenuItem undoMenuItem; OsuMenuItem redoMenuItem; EditorMenuItem cutMenuItem; EditorMenuItem copyMenuItem; EditorMenuItem pasteMenuItem; var fileMenuItems = new List <MenuItem> { new EditorMenuItem("Save", MenuItemType.Standard, Save) }; if (RuntimeInfo.IsDesktop) { fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap)); } fileMenuItems.Add(new EditorMenuItemSpacer()); fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit)); AddInternal(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, Children = new[] { new Container { Name = "Screen container", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 40, Bottom = 60 }, Child = screenContainer = new Container <EditorScreen> { RelativeSizeAxes = Axes.Both, Masking = true } }, new Container { Name = "Top bar", RelativeSizeAxes = Axes.X, Height = 40, Child = menuBar = new EditorMenuBar { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, RelativeSizeAxes = Axes.Both, Mode = { Value = isNewBeatmap ? EditorScreenMode.SongSetup : EditorScreenMode.Compose }, Items = new[] { new MenuItem("File") { Items = fileMenuItems }, new MenuItem("Edit") { Items = new[] { undoMenuItem = new EditorMenuItem("Undo", MenuItemType.Standard, Undo), redoMenuItem = new EditorMenuItem("Redo", MenuItemType.Standard, Redo), new EditorMenuItemSpacer(), cutMenuItem = new EditorMenuItem("Cut", MenuItemType.Standard, Cut), copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy), pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste), } }, new MenuItem("View") { Items = new[] { new WaveformOpacityMenu(config) } } } } }, new Container { Name = "Bottom bar", Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, Height = 60, Children = new Drawable[] { bottomBackground = new Box { RelativeSizeAxes = Axes.Both }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Vertical = 5, Horizontal = 10 }, Child = new GridContainer { RelativeSizeAxes = Axes.Both, ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, 220), new Dimension(), new Dimension(GridSizeMode.Absolute, 220) }, Content = new[] { new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Right = 10 }, Child = new TimeInfoContainer { RelativeSizeAxes = Axes.Both }, }, new SummaryTimeline { RelativeSizeAxes = Axes.Both, }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = 10 }, Child = new PlaybackControl { RelativeSizeAxes = Axes.Both }, } }, } }, } } }, } }); changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true); changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true); editorBeatmap.SelectedHitObjects.BindCollectionChanged((_, __) => { var hasObjects = editorBeatmap.SelectedHitObjects.Count > 0; cutMenuItem.Action.Disabled = !hasObjects; copyMenuItem.Action.Disabled = !hasObjects; }, true); clipboard.BindValueChanged(content => pasteMenuItem.Action.Disabled = string.IsNullOrEmpty(content.NewValue)); menuBar.Mode.ValueChanged += onModeChanged; bottomBackground.Colour = colours.Gray2; }