ui for switching between subtasks and simple subtask switcher
authorLiana Bakradze <liana.bakradze@jetbrains.com>
Thu, 22 Sep 2016 11:23:29 +0000 (14:23 +0300)
committerliana.bakradze <liana.bakradze@jetbrains.com>
Thu, 17 Nov 2016 14:05:03 +0000 (17:05 +0300)
python/educational-core/course-creator/resources/META-INF/plugin.xml
python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java [new file with mode: 0644]
python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCNewSubtaskAction.java
python/educational-core/student/src/com/jetbrains/edu/learning/StudySubtaskUtils.java [new file with mode: 0644]
python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java
python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Task.java
python/educational-python/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCLanguageManager.java

index 134f7f4d4b28b530a103bdab9dfa50f67487c594..797eace2e67d099d91a0cc77fb3e62165006be8f 100644 (file)
@@ -26,6 +26,7 @@
     <applicationService serviceInterface="com.jetbrains.edu.coursecreator.settings.CCSettings"
                         serviceImplementation="com.jetbrains.edu.coursecreator.settings.CCSettings"/>
     <highlightErrorFilter implementation="com.jetbrains.edu.coursecreator.CCHighlightErrorFilter"/>
+    <editorNotificationProvider implementation="com.jetbrains.edu.coursecreator.CCSubtaskEditorNotificationProvider"/>
   </extensions>
   <extensions defaultExtensionNs="Edu">
     <studyActionsProvider implementation="com.jetbrains.edu.coursecreator.CCStudyActionsProvider"/>
diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCSubtaskEditorNotificationProvider.java
new file mode 100644 (file)
index 0000000..3663afa
--- /dev/null
@@ -0,0 +1,169 @@
+package com.jetbrains.edu.coursecreator;
+
+import com.intellij.openapi.editor.colors.EditorColors;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.ui.popup.ListSeparator;
+import com.intellij.openapi.ui.popup.PopupStep;
+import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.EditorNotificationPanel;
+import com.intellij.ui.EditorNotifications;
+import com.intellij.ui.awt.RelativePoint;
+import com.jetbrains.edu.coursecreator.actions.CCNewSubtaskAction;
+import com.jetbrains.edu.learning.StudySubtaskUtils;
+import com.jetbrains.edu.learning.StudyUtils;
+import com.jetbrains.edu.learning.core.EduNames;
+import com.jetbrains.edu.learning.courseFormat.Task;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class CCSubtaskEditorNotificationProvider extends EditorNotifications.Provider<EditorNotificationPanel> implements DumbAware {
+  private static final Key<EditorNotificationPanel> KEY = Key.create("edu.coursecreator.subtask");
+  public static final String SWITCH_SUBTASK = "Switch subtask";
+  public static final Integer ADD_SUBTASK_ID = -1;
+  private final Project myProject;
+
+  public CCSubtaskEditorNotificationProvider(Project project) {
+    myProject = project;
+  }
+
+  @NotNull
+  @Override
+  public Key<EditorNotificationPanel> getKey() {
+    return KEY;
+  }
+
+  @Nullable
+  @Override
+  public EditorNotificationPanel createNotificationPanel(@NotNull VirtualFile file, @NotNull FileEditor fileEditor) {
+    if (!CCUtils.isCourseCreator(myProject)) {
+      return null;
+    }
+    boolean isTestFile = CCUtils.isTestsFile(myProject, file);
+    if (!isTestFile && StudyUtils.getTaskFile(myProject, file) == null) {
+      return null;
+    }
+    Task task = StudyUtils.getTaskForFile(myProject, file);
+    if (task == null || !task.hasSubtasks()) {
+      return null;
+    }
+    EditorNotificationPanel panel = new EditorNotificationPanel() {
+      @Override
+      public Color getBackground() {
+        Color color = EditorColorsManager.getInstance().getGlobalScheme().getColor(EditorColors.GUTTER_BACKGROUND);
+        return color == null ? super.getBackground() : color;
+      }
+    };
+    String header = isTestFile ? "test" : "task file";
+    int activeSubtaskIndex = task.getActiveSubtaskIndex() + 1;
+    panel.setText("This is " + header + " for " + EduNames.SUBTASK + " " + activeSubtaskIndex + "/" + task.getSubtaskNum());
+    panel.createActionLabel(SWITCH_SUBTASK, () -> {
+      ArrayList<Integer> values = new ArrayList<>();
+      for (int i = 0; i < task.getSubtaskNum(); i++) {
+        values.add(i);
+      }
+      values.add(ADD_SUBTASK_ID);
+      JBPopupFactory.getInstance().createListPopup(new SwitchSubtaskPopupStep(SWITCH_SUBTASK, values, task, file)).show(RelativePoint.getSouthEastOf(panel));
+    });
+    return panel;
+  }
+
+  private class SwitchSubtaskPopupStep extends BaseListPopupStep<Integer> {
+    private final Task myTask;
+    private final VirtualFile myFile;
+
+    public SwitchSubtaskPopupStep(@Nullable String title,
+                                  List<Integer> values,
+                                  Task task, VirtualFile file) {
+      super(title, values);
+      myTask = task;
+      myFile = file;
+    }
+
+    @NotNull
+    @Override
+    public String getTextFor(Integer value) {
+      if (value.equals(ADD_SUBTASK_ID)) {
+        return CCNewSubtaskAction.NEW_SUBTASK;
+      }
+      int subtaskNum = value + 1;
+      String text = EduNames.SUBTASK + " " + subtaskNum;
+      if (value == myTask.getActiveSubtaskIndex()) {
+        text +=  " (selected)";
+      }
+      return text;
+    }
+
+    @Override
+    public PopupStep onChosen(Integer selectedValue, boolean finalChoice) {
+      if (finalChoice) {
+        if (selectedValue.equals(ADD_SUBTASK_ID)) {
+          return doFinalStep(() -> CCNewSubtaskAction.addSubtask(myFile, myProject));
+        }
+        StudySubtaskUtils.switchStep(myProject, myTask, selectedValue);
+      } else {
+        if (hasSubstep(selectedValue)) {
+          return new ActionsPopupStep(myTask, selectedValue);
+        }
+      }
+      return super.onChosen(selectedValue, false);
+    }
+
+    @Override
+    public boolean hasSubstep(Integer selectedValue) {
+      return !selectedValue.equals(ADD_SUBTASK_ID);
+    }
+
+    @Override
+    public int getDefaultOptionIndex() {
+      return myTask.getActiveSubtaskIndex() + 1;
+    }
+
+    @Nullable
+    @Override
+    public ListSeparator getSeparatorAbove(Integer value) {
+      return value.equals(ADD_SUBTASK_ID) ? new ListSeparator() : null;
+    }
+  }
+
+  private class ActionsPopupStep extends BaseListPopupStep<String> {
+
+    public static final String SELECT = "Select";
+    public static final String DELETE = "Delete";
+    private final Task myTask;
+    private final int mySubtaskIndex;
+
+    public ActionsPopupStep(Task task, int subtaskIndex) {
+      super(null, Arrays.asList(SELECT, DELETE));
+      myTask = task;
+      mySubtaskIndex = subtaskIndex;
+    }
+
+    @Override
+    public PopupStep onChosen(String selectedValue, boolean finalChoice) {
+      if (finalChoice) {
+        if (selectedValue.equals(SELECT)) {
+          StudySubtaskUtils.switchStep(myProject, myTask, mySubtaskIndex);
+        } else {
+          if (mySubtaskIndex != myTask.getSubtaskNum() - 1) {
+            //TODO: implement
+          } else {
+            //TODO: delete last subtask
+          }
+          return FINAL_CHOICE;
+        }
+      }
+      return super.onChosen(selectedValue, finalChoice);
+    }
+  }
+}
\ No newline at end of file
index 5f84c1bf25b0bc99780067764f845c098d9d7cb8..57bc3761b06f35c432c9a521f79f507e3e639f26 100644 (file)
@@ -19,6 +19,7 @@ import com.intellij.psi.PsiManager;
 import com.jetbrains.edu.coursecreator.CCLanguageManager;
 import com.jetbrains.edu.coursecreator.CCUtils;
 import com.jetbrains.edu.coursecreator.settings.CCSettings;
+import com.jetbrains.edu.learning.StudySubtaskUtils;
 import com.jetbrains.edu.learning.StudyTaskManager;
 import com.jetbrains.edu.learning.StudyUtils;
 import com.jetbrains.edu.learning.core.EduNames;
@@ -63,7 +64,7 @@ public class CCNewSubtaskAction extends DumbAwareAction {
     createTaskDescriptionFile(project, taskDir, num);
     task.setSubtaskNum(num + 1);
     task.setActiveSubtaskIndex(num);
-    //TODO: switch subtask
+    StudySubtaskUtils.switchStep(project, task, num);
   }
 
   private static void createTestsForNewSubtask(Project project, Task task) {
diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudySubtaskUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudySubtaskUtils.java
new file mode 100644 (file)
index 0000000..81072ab
--- /dev/null
@@ -0,0 +1,57 @@
+package com.jetbrains.edu.learning;
+
+import com.intellij.ide.projectView.ProjectView;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.EditorNotifications;
+import com.jetbrains.edu.learning.checker.StudyCheckUtils;
+import com.jetbrains.edu.learning.core.EduNames;
+import com.jetbrains.edu.learning.courseFormat.Task;
+import com.jetbrains.edu.learning.courseFormat.TaskFile;
+import com.jetbrains.edu.learning.ui.StudyToolWindow;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Map;
+
+public class StudySubtaskUtils {
+  private StudySubtaskUtils() {
+  }
+
+  /***
+   * @param toSubtaskIndex from 0 to subtaskNum - 1
+   */
+  public static void switchStep(@NotNull Project project, @NotNull Task task, int toSubtaskIndex) {
+    VirtualFile taskDir = task.getTaskDir(project);
+    if (taskDir == null) {
+      return;
+    }
+    VirtualFile srcDir = taskDir.findChild(EduNames.SRC);
+    if (srcDir != null) {
+      taskDir = srcDir;
+    }
+    for (Map.Entry<String, TaskFile> entry : task.getTaskFiles().entrySet()) {
+      String name = entry.getKey();
+      VirtualFile virtualFile = taskDir.findChild(name);
+      if (virtualFile == null) {
+        continue;
+      }
+      EditorNotifications.getInstance(project).updateNotifications(virtualFile);
+    }
+    task.setActiveSubtaskIndex(toSubtaskIndex);
+    update(project, task, taskDir);
+  }
+
+  private static void update(@NotNull Project project, @NotNull Task task, VirtualFile taskDir) {
+    StudyCheckUtils.drawAllPlaceholders(project, task, taskDir);
+    ProjectView.getInstance(project).refresh();
+    StudyToolWindow toolWindow = StudyUtils.getStudyToolWindow(project);
+    if (toolWindow != null) {
+      String text = StudyUtils.getTaskTextFromTask(taskDir, task);
+      if (text == null) {
+        toolWindow.setEmptyText(project);
+        return;
+      }
+      toolWindow.setTaskText(text, taskDir, project);
+    }
+  }
+}
\ No newline at end of file
index 0d73f5c58e7398894486eb03ca1dd3ba659f5571..b20719033090c3a414b4b0cba4a30d77f09b5e60 100644 (file)
@@ -50,6 +50,7 @@ public class EduNames {
   public static final String SRC = "src";
 
   public static final String SUBTASK_MARKER = "_subtask";
+  public static final String SUBTASK = "subtask";
 
   private EduNames() {
   }
index 82c42c934f3488626f5b8283464d43f7dc3ecef4..f9c804bca5fe85ab654d64a68cf2e8d5f0c95065 100644 (file)
@@ -269,4 +269,8 @@ public class Task implements StudyItem {
   public void setSubtaskNum(int subtaskNum) {
     mySubtaskNum = subtaskNum;
   }
+
+  public boolean hasSubtasks() {
+    return mySubtaskNum > 1;
+  }
 }
index f9586c66bca1c651f8536cf61ed22efd83343318..d0c342227b94f3ad0775ae416f59f9631b0f2d1d 100644 (file)
@@ -70,7 +70,7 @@ public class PyCCLanguageManager implements CCLanguageManager {
       return;
     }
 
-    int prevSubtaskIndex = task.getActiveSubtaskIndex();
+    int prevSubtaskIndex = task.getSubtaskNum() - 1;
     String name = prevSubtaskIndex == 0 ? EduNames.TESTS_FILE : getSubtaskTestsFileName(prevSubtaskIndex);
     VirtualFile testsFile = taskDir.findChild(name);
     if (testsFile == null) {