From d8987e4a03d92ac91fb54a5fb350da85e0a4f58f Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Thu, 8 Aug 2019 20:45:28 +0200 Subject: [PATCH] OO-4168: allow to stack tools in the toolbar automatically --- .../segmentedview/SegmentViewRenderer.java | 1 + .../stack/BreadcrumbBarRenderer.java | 82 +++++++ .../stack/BreadcrumbedStackedPanel.java | 142 ++++++++++- .../BreadcrumbedStackedPanelRenderer.java | 31 +-- .../gui/components/stack/ToolBarRenderer.java | 135 ++++++++++ .../components/stack/TooledStackedPanel.java | 231 ++++++++++++------ .../stack/TooledStackedPanelRenderer.java | 123 +--------- .../org/olat/core/gui/render/Renderer.java | 19 +- 8 files changed, 525 insertions(+), 239 deletions(-) create mode 100644 src/main/java/org/olat/core/gui/components/stack/BreadcrumbBarRenderer.java create mode 100644 src/main/java/org/olat/core/gui/components/stack/ToolBarRenderer.java diff --git a/src/main/java/org/olat/core/gui/components/segmentedview/SegmentViewRenderer.java b/src/main/java/org/olat/core/gui/components/segmentedview/SegmentViewRenderer.java index 432c24caac4..881e76c8ac4 100644 --- a/src/main/java/org/olat/core/gui/components/segmentedview/SegmentViewRenderer.java +++ b/src/main/java/org/olat/core/gui/components/segmentedview/SegmentViewRenderer.java @@ -42,6 +42,7 @@ public class SegmentViewRenderer extends DefaultComponentRenderer { ComponentRenderer subRenderer = segment.getHTMLRendererSingleton(); Translator subTranslator = segment.getTranslator(); subRenderer.render(renderer, sb, segment, ubu, subTranslator, renderResult, args); + segment.setDirty(false); } sb.append("</div>"); } diff --git a/src/main/java/org/olat/core/gui/components/stack/BreadcrumbBarRenderer.java b/src/main/java/org/olat/core/gui/components/stack/BreadcrumbBarRenderer.java new file mode 100644 index 00000000000..92128b1268b --- /dev/null +++ b/src/main/java/org/olat/core/gui/components/stack/BreadcrumbBarRenderer.java @@ -0,0 +1,82 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.gui.components.stack; + +import java.util.List; + +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.DefaultComponentRenderer; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.stack.BreadcrumbedStackedPanel.BreadcrumbBar; +import org.olat.core.gui.render.RenderResult; +import org.olat.core.gui.render.Renderer; +import org.olat.core.gui.render.StringOutput; +import org.olat.core.gui.render.URLBuilder; +import org.olat.core.gui.translator.Translator; + +/** + * + * Initial date: 7 août 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class BreadcrumbBarRenderer extends DefaultComponentRenderer { + + @Override + public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator, + RenderResult renderResult, String[] args) { + + BreadcrumbBar bar = (BreadcrumbBar)source; + BreadcrumbedStackedPanel panel = bar.getPanel(); + List<Link> breadCrumbs = panel.getBreadCrumbs(); + + if (breadCrumbs.size() > panel.getInvisibleCrumb()) { + sb.append("<div id='o_c").append(source.getDispatchID()).append("' class='o_breadcrumb'>") + .append("<ol class='breadcrumb'>"); + + Link backLink = panel.getBackLink(); + int numOfCrumbs = breadCrumbs.size(); + if(backLink.isVisible() && numOfCrumbs > panel.getInvisibleCrumb()) { + sb.append("<li class='o_breadcrumb_back'>"); + backLink.getHTMLRendererSingleton().render(renderer, sb, backLink, ubu, translator, renderResult, args); + backLink.setDirty(false); + sb.append("</li>"); + + for(Link crumb:breadCrumbs) { + sb.append("<li>"); + renderer.render(crumb, sb, args); + sb.append("</li>"); + } + } + + Link closeLink = panel.getCloseLink(); + if (closeLink.isVisible()) { + sb.append("<li class='o_breadcrumb_close'>"); + closeLink.getHTMLRendererSingleton().render(renderer, sb, closeLink, ubu, translator, renderResult, args); + closeLink.setDirty(false); + sb.append("</li>"); + } + sb.append("</ol>"); + } else { + sb.append("<div id='o_c").append(source.getDispatchID()).append("'>"); + } + sb.append("</div>"); + } +} diff --git a/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanel.java b/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanel.java index b9b91209e9b..d17b5fd662e 100644 --- a/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanel.java +++ b/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanel.java @@ -24,13 +24,16 @@ import java.util.List; import org.apache.logging.log4j.Logger; import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.AbstractComponent; import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.ComponentCollection; import org.olat.core.gui.components.ComponentEventListener; import org.olat.core.gui.components.ComponentRenderer; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.panel.Panel; import org.olat.core.gui.components.panel.StackedPanel; +import org.olat.core.gui.components.stack.TooledStackedPanel.Align; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; @@ -49,14 +52,16 @@ import org.olat.core.util.Util; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class BreadcrumbedStackedPanel extends Panel implements StackedPanel, BreadcrumbPanel, ComponentEventListener { +public class BreadcrumbedStackedPanel extends Panel implements BreadcrumbPanel, ComponentEventListener { private static final Logger log = Tracing.createLoggerFor(BreadcrumbedStackedPanel.class); + private static final ComponentRenderer BAR_RENDERER = new BreadcrumbBarRenderer(); private static final ComponentRenderer RENDERER = new BreadcrumbedStackedPanelRenderer(); protected final List<Link> stack = new ArrayList<>(3); protected final Link backLink; protected final Link closeLink; + protected final BreadcrumbBar breadcrumbBar; private int invisibleCrumb = 1; private String cssClass; @@ -75,6 +80,9 @@ public class BreadcrumbedStackedPanel extends Panel implements StackedPanel, Bre this.cssClass = cssClass; + String barId = getDispatchID().concat("_bbar"); + breadcrumbBar = new BreadcrumbBar(barId); + // Add back link before the bread crumbs, when pressed delegates click to current bread-crumb - 1 backLink = LinkFactory.createCustomLink("back", "back", "\u00A0", Link.NONTRANSLATED + Link.LINK_CUSTOM_CSS, null, this); backLink.setIconLeftCSS("o_icon o_icon_back"); @@ -174,19 +182,27 @@ public class BreadcrumbedStackedPanel extends Panel implements StackedPanel, Bre public List<Link> getBreadCrumbs() { return stack; } + + public BreadcrumbBar getBreadcrumbBar() { + return breadcrumbBar; + } + + @Override + public Component getComponent(String name) { + if(breadcrumbBar.getComponentName().equals(name)) { + return breadcrumbBar; + } + return super.getComponent(name); + } @Override public Iterable<Component> getComponents() { List<Component> cmps = new ArrayList<>(3 + stack.size()); - cmps.add(backLink); - cmps.add(closeLink); + cmps.add(breadcrumbBar); Component content = getContent(); if(content != null && content != this) { cmps.add(getContent()); } - for(Link crumb:stack) { - cmps.add(crumb); - } return cmps; } @@ -408,7 +424,7 @@ public class BreadcrumbedStackedPanel extends Panel implements StackedPanel, Bre @Override public void rootController(String displayName, Controller controller) { - if(stack.size() > 0) { + if(!stack.isEmpty()) { for(int i=stack.size(); i-->0; ) { Link link = stack.remove(i); BreadCrumb crumb = (BreadCrumb)link.getUserObject(); @@ -493,7 +509,7 @@ public class BreadcrumbedStackedPanel extends Panel implements StackedPanel, Bre @Override public void changeDisplayname(String diplayName) { stack.get(stack.size() - 1).setCustomDisplayText(diplayName); - setDirty(true); + breadcrumbBar.setDirty(true); } @Override @@ -584,9 +600,72 @@ public class BreadcrumbedStackedPanel extends Panel implements StackedPanel, Bre closeLink.setVisible(showClose); } + public class BreadcrumbBar extends AbstractComponent implements ComponentCollection { + + public BreadcrumbBar(String id) { + super(id, null, null); + setDomReplacementWrapperRequired(false); + } + + public BreadcrumbedStackedPanel getPanel() { + return BreadcrumbedStackedPanel.this; + } + + @Override + public Translator getTranslator() { + return BreadcrumbedStackedPanel.this.getTranslator(); + } + + @Override + protected void doDispatchRequest(UserRequest ureq) { + String cmd = ureq.getParameter(VelocityContainer.COMMAND_ID); + if(cmd != null) { + if(backLink.getCommand().equals(cmd)) { + dispatchEvent(ureq, backLink, null); + } else if(closeLink.getCommand().equals(cmd)) { + dispatchEvent(ureq, closeLink, null); + } + } + } + + @Override + public ComponentRenderer getHTMLRendererSingleton() { + return BAR_RENDERER; + } + + @Override + public Component getComponent(String name) { + if(backLink.getComponentName().equals(name)) { + return backLink; + } + if(closeLink.getComponentName().equals(name)) { + return closeLink; + } + for(Link crumb:stack) { + if(crumb != null && crumb.getComponentName().equals(name)) { + return crumb; + } + } + return null; + } + + @Override + public Iterable<Component> getComponents() { + List<Component> cmps = new ArrayList<>(3 + stack.size()); + cmps.add(backLink); + cmps.add(closeLink); + for(Link crumb:stack) { + cmps.add(crumb); + } + return cmps; + } + } + public static class BreadCrumb { + private final Object uobject; private final Controller controller; + private final List<Tool> tools = new ArrayList<>(5); public BreadCrumb(Controller controller, Object uobject) { this.uobject = uobject; @@ -596,15 +675,58 @@ public class BreadcrumbedStackedPanel extends Panel implements StackedPanel, Bre public Object getUserObject() { return uobject; } - + public Controller getController() { return controller; } - + + public List<Tool> getTools() { + return tools; + } + + public void addTool(Tool tool) { + tools.add(tool); + } + + public void removeTool(Tool tool) { + tools.remove(tool); + } + public void dispose() { if(controller != null) { controller.dispose(); } } } + + public static class Tool { + + private final Align align; + private final boolean inherit; + private final Component component; + private String toolCss; + + public Tool(Component component, Align align, boolean inherit, String toolCss) { + this.align = align; + this.inherit = inherit; + this.component = component; + this.toolCss = toolCss; + } + + public boolean isInherit() { + return inherit; + } + + public Align getAlign() { + return align; + } + + public Component getComponent() { + return component; + } + + public String getToolCss() { + return toolCss; + } + } } \ No newline at end of file diff --git a/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanelRenderer.java b/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanelRenderer.java index 5eef3c988ce..b00e50a8a01 100644 --- a/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanelRenderer.java +++ b/src/main/java/org/olat/core/gui/components/stack/BreadcrumbedStackedPanelRenderer.java @@ -19,11 +19,8 @@ */ package org.olat.core.gui.components.stack; -import java.util.List; - import org.olat.core.gui.components.Component; import org.olat.core.gui.components.DefaultComponentRenderer; -import org.olat.core.gui.components.link.Link; import org.olat.core.gui.render.RenderResult; import org.olat.core.gui.render.Renderer; import org.olat.core.gui.render.StringOutput; @@ -42,39 +39,13 @@ public class BreadcrumbedStackedPanelRenderer extends DefaultComponentRenderer { public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator, RenderResult renderResult, String[] args) { BreadcrumbedStackedPanel panel = (BreadcrumbedStackedPanel) source; - List<Link> breadCrumbs = panel.getBreadCrumbs(); // panel div String mainCssClass = panel.getCssClass(); sb.append("<div id='o_c").append(source.getDispatchID()).append("' class='") .append(mainCssClass, mainCssClass != null).append("'>"); - if (breadCrumbs.size() > panel.getInvisibleCrumb()) { - sb.append("<div class='o_breadcrumb'><ol class='breadcrumb'>"); - - Link backLink = panel.getBackLink(); - int numOfCrumbs = breadCrumbs.size(); - if(backLink.isVisible() && numOfCrumbs > panel.getInvisibleCrumb()) { - sb.append("<li class='o_breadcrumb_back'>"); - backLink.getHTMLRendererSingleton().render(renderer, sb, backLink, ubu, translator, renderResult, args); - sb.append("</li>"); - - for(Link crumb:breadCrumbs) { - sb.append("<li>"); - renderer.render(crumb, sb, args); - sb.append("</li>"); - } - } - - Link closeLink = panel.getCloseLink(); - if (closeLink.isVisible()) { - sb.append("<li class='o_breadcrumb_close'>"); - closeLink.getHTMLRendererSingleton().render(renderer, sb, closeLink, ubu, translator, renderResult, args); - sb.append("</li>"); - } - - sb.append("</ol></div>"); - } + renderer.render(panel.getBreadcrumbBar(), sb, args); Component toRender = panel.getContent(); if(toRender != null) { diff --git a/src/main/java/org/olat/core/gui/components/stack/ToolBarRenderer.java b/src/main/java/org/olat/core/gui/components/stack/ToolBarRenderer.java new file mode 100644 index 00000000000..b873a97c35e --- /dev/null +++ b/src/main/java/org/olat/core/gui/components/stack/ToolBarRenderer.java @@ -0,0 +1,135 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.core.gui.components.stack; + +import java.util.List; + +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.DefaultComponentRenderer; +import org.olat.core.gui.components.dropdown.Dropdown; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.stack.BreadcrumbedStackedPanel.Tool; +import org.olat.core.gui.components.stack.TooledStackedPanel.Align; +import org.olat.core.gui.components.stack.TooledStackedPanel.ToolBar; +import org.olat.core.gui.components.stack.TooledStackedPanel.ToolsSlot; +import org.olat.core.gui.render.RenderResult; +import org.olat.core.gui.render.Renderer; +import org.olat.core.gui.render.StringOutput; +import org.olat.core.gui.render.URLBuilder; +import org.olat.core.gui.translator.Translator; + +/** + * + * Initial date: 7 août 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class ToolBarRenderer extends DefaultComponentRenderer { + + @Override + public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator, + RenderResult renderResult, String[] args) { + + ToolBar toolBar = (ToolBar)source; + TooledStackedPanel panel = toolBar.getPanel(); + List<Tool> leftTools = panel.getTools(Align.left); + List<Tool> rightEdgeTools = panel.getTools(Align.rightEdge); + List<Tool> rightTools = panel.getTools(Align.right); + List<Tool> segmentsTools = panel.getTools(Align.segment); + List<Tool> centerTools = panel.getTools(Align.center); + + if(panel.isToolbarEnabled() || (panel.isToolbarAutoEnabled() + && (!leftTools.isEmpty() || !rightTools.isEmpty() || !centerTools.isEmpty() || !segmentsTools.isEmpty()))) { + sb.append("<div id='o_c").append(source.getDispatchID()).append("' class='o_tools_container'><div class='container-fluid'>"); + + renderTools(leftTools, renderer, toolBar.getSlot(Align.left), sb, translator, args); + renderTools(rightEdgeTools, renderer, toolBar.getSlot(Align.rightEdge), sb, translator, args); + renderTools(rightTools, renderer, toolBar.getSlot(Align.right), sb, translator, args); + renderTools(centerTools, renderer, toolBar.getSlot(Align.center), sb, translator, args); + + sb.append("</div>"); // container-fluid, + + if(!segmentsTools.isEmpty()) { + boolean segmentAlone = leftTools.isEmpty() && rightTools.isEmpty() && centerTools.isEmpty(); + sb.append("<ul class='o_tools o_tools_segments list-inline") + .append(" o_tools_segments_alone", segmentAlone).append("'>"); + + Tool segmentTool = segmentsTools.get(segmentsTools.size() - 1); + renderTool(segmentTool, renderer, sb, args); + + sb.append("</ul>"); + } + sb.append("</div>"); + } else { + sb.append("<div id='o_c").append(source.getDispatchID()).append("'></div>"); + } + } + + private void renderTools(List<Tool> tools, Renderer renderer, ToolsSlot slot, StringOutput sb, Translator translator, String[] args) { + if(!tools.isEmpty()) { + Align align = slot.getSlot(); + sb.append("<ul class='o_tools ").append(align.cssClass()).append(" list-inline'>"); + + int limit = slot.getLimitOfTools(); + for(int i=0; i<tools.size() && i<limit-1; i++) { + renderTool(tools.get(i), renderer, sb, args); + } + if(tools.size() > limit) { + List<Tool> droppedTools = tools.subList(limit - 1, tools.size()); + renderDropDown(droppedTools, renderer, slot, sb, translator, args); + } + sb.append("</ul>"); + } + } + + private void renderDropDown(List<Tool> tools, Renderer renderer, ToolsSlot slot, StringOutput sb, Translator translator, String[] args) { + String label = translator == null ? slot.getToolDropdownI18nKey() : translator.translate(slot.getToolDropdownI18nKey());// paranoia + + sb.append("<li class='o_tool_dropdown dropdown'>") + .append("<a href='#' class='dropdown-toggle' data-toggle='dropdown'>") + .append("<span class='o_inner_wrapper'><i class='o_icon o_icon_tools'>\u00A0</i></span> <i class='o_icon o_icon_caret'> </i> <span class='o_label'>") + .append(label).append("</span></a>") + .append("<ul class='dropdown-menu").append(" dropdown-menu-right", slot.getSlot() == Align.right || slot.getSlot() == Align.rightEdge).append("' role='menu'>"); + for(Tool tool:tools) { + sb.append("<li>"); + renderer.render(tool.getComponent(), sb, args); + sb.append("</li>"); + } + sb.append("</ul></li>"); + } + + private void renderTool(Tool tool, Renderer renderer, StringOutput sb, String[] args) { + Component cmp = tool.getComponent(); + String cssClass = tool.getToolCss(); + if (cssClass == null) { + // use defaults + if(cmp instanceof Dropdown) { + cssClass = "o_tool_dropdown dropdown"; + } else if(cmp instanceof Link && !cmp.isEnabled()) { + cssClass = "o_text"; + } else { + cssClass = "o_tool"; + } + } + sb.append("<li class='").append(cssClass).append("'>"); + renderer.render(cmp, sb, args); + sb.append("</li>"); + } +} diff --git a/src/main/java/org/olat/core/gui/components/stack/TooledStackedPanel.java b/src/main/java/org/olat/core/gui/components/stack/TooledStackedPanel.java index d99c9aced8e..ed506d5f0f3 100644 --- a/src/main/java/org/olat/core/gui/components/stack/TooledStackedPanel.java +++ b/src/main/java/org/olat/core/gui/components/stack/TooledStackedPanel.java @@ -20,16 +20,20 @@ package org.olat.core.gui.components.stack; import java.util.ArrayList; +import java.util.EnumMap; import java.util.Iterator; import java.util.List; +import java.util.stream.Collectors; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.AbstractComponent; import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.ComponentCollection; import org.olat.core.gui.components.ComponentEventListener; import org.olat.core.gui.components.ComponentRenderer; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.panel.StackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.translator.Translator; +import org.olat.core.util.StringHelper; /** * @@ -39,9 +43,10 @@ import org.olat.core.gui.translator.Translator; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class TooledStackedPanel extends BreadcrumbedStackedPanel implements StackedPanel, BreadcrumbPanel, ComponentEventListener { +public class TooledStackedPanel extends BreadcrumbedStackedPanel { private static final ComponentRenderer RENDERER = new TooledStackedPanelRenderer(); + private static final ComponentRenderer TOOLS_RENDERER = new ToolBarRenderer(); private boolean toolbarEnabled = true; private boolean toolbarAutoEnabled = false; private boolean breadcrumbEnabled = true; @@ -49,6 +54,8 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac private String message; private String messageCssClass; private Component messageCmp; + private final ToolBar toolBar; + private final EnumMap<Align,ToolsSlot> toolsSlots; public TooledStackedPanel(String name, Translator translator, ComponentEventListener listener) { this(name, translator, listener, null); @@ -57,22 +64,26 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac public TooledStackedPanel(String name, Translator translator, ComponentEventListener listener, String cssClass) { super(name, translator, listener, cssClass); setDomReplacementWrapperRequired(false); // renders own div in Renderer + toolsSlots = new EnumMap<>(Align.class); + for(Align val:Align.values()) { + toolsSlots.put(val, new ToolsSlot(val)); + } + toolBar = new ToolBar(getDispatchID().concat("_tbar")); + } + + public ToolBar getToolBar() { + return toolBar; } @Override public Iterable<Component> getComponents() { List<Component> cmps = new ArrayList<>(); - cmps.add(getBackLink()); + cmps.add(getBreadcrumbBar()); cmps.add(getContent()); + cmps.add(toolBar); if(messageCmp != null) { cmps.add(messageCmp); } - for(Link crumb:stack) { - cmps.add(crumb); - } - for(Tool tool:getTools()) { - cmps.add(tool.getComponent()); - } return cmps; } @@ -83,7 +94,7 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac @Override protected BreadCrumb createCrumb(Controller controller, Object uobject) { - return new TooledBreadCrumb(controller, uobject); + return new BreadCrumb(controller, uobject); } /** @@ -122,7 +133,7 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac public void removeTool(Component toolComponent) { if(toolComponent == null) return; - TooledBreadCrumb breadCrumb = getCurrentCrumb(); + BreadCrumb breadCrumb = getCurrentCrumb(); if(breadCrumb != null) { removeTool(toolComponent, breadCrumb); } @@ -131,8 +142,8 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac public void removeTool(Component toolComponent, Controller controller) { for(int i=0; i<stack.size(); i++) { Object uo = stack.get(i).getUserObject(); - if(uo instanceof TooledBreadCrumb) { - TooledBreadCrumb crumb = (TooledBreadCrumb)uo; + if(uo instanceof BreadCrumb) { + BreadCrumb crumb = (BreadCrumb)uo; if (controller.equals(crumb.getController())) { removeTool(toolComponent, crumb); } @@ -140,21 +151,21 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac } } - private void removeTool(Component toolComponent, TooledBreadCrumb breadCrumb) { + private void removeTool(Component toolComponent, BreadCrumb breadCrumb) { for(Iterator<Tool> it=breadCrumb.getTools().iterator(); it.hasNext(); ) { if(toolComponent == it.next().getComponent()) { it.remove(); - setDirty(true); + toolBar.setDirty(true); } } } public void removeAllTools() { - TooledBreadCrumb breadCrumb = getCurrentCrumb(); + BreadCrumb breadCrumb = getCurrentCrumb(); if(breadCrumb != null) { breadCrumb.getTools().clear(); } - setDirty(true); + toolBar.setDirty(true); } /** @@ -168,21 +179,22 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac public void addTool(Component toolComponent, Align align, boolean inherit, String css, Controller controller) { if(toolComponent == null) return; + align = align == null ? Align.center : align; Tool tool = new Tool(toolComponent, align, inherit, css); - TooledBreadCrumb breadCrumb = controller == null + BreadCrumb breadCrumb = controller == null ? getCurrentCrumb() : getBreadCrumb(controller); if(breadCrumb != null) { breadCrumb.addTool(tool); } - setDirty(true); + toolBar.setDirty(true); } - - private TooledBreadCrumb getBreadCrumb(Controller controller) { + + private BreadCrumb getBreadCrumb(Controller controller) { for(int i=0; i<stack.size(); i++) { Object uo = stack.get(i).getUserObject(); - if(uo instanceof TooledBreadCrumb) { - TooledBreadCrumb crumb = (TooledBreadCrumb)uo; + if(uo instanceof BreadCrumb) { + BreadCrumb crumb = (BreadCrumb)uo; if (controller.equals(crumb.getController())) { return crumb; } @@ -197,8 +209,8 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac int lastStep = stack.size() - 1; for(int i=0; i<lastStep; i++) { Object uo = stack.get(i).getUserObject(); - if(uo instanceof TooledBreadCrumb) { - TooledBreadCrumb crumb = (TooledBreadCrumb)uo; + if(uo instanceof BreadCrumb) { + BreadCrumb crumb = (BreadCrumb)uo; List<Tool> tools = crumb.getTools(); for(Tool tool:tools) { if(tool.isInherit()) { @@ -208,18 +220,25 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac } } - TooledBreadCrumb breadCrumb = getCurrentCrumb(); + BreadCrumb breadCrumb = getCurrentCrumb(); if(breadCrumb != null) { currentTools.addAll(breadCrumb.getTools()); } return currentTools; } - private TooledBreadCrumb getCurrentCrumb() { + public List<Tool> getTools(Align alignement) { + List<Tool> tools = getTools(); + return tools.stream() + .filter(tool -> alignement.equals(tool.getAlign()) && tool.getComponent().isVisible()) + .collect(Collectors.toList()); + } + + private BreadCrumb getCurrentCrumb() { if(stack.isEmpty()) { return null; } - return (TooledBreadCrumb)stack.get(stack.size() - 1).getUserObject(); + return (BreadCrumb)stack.get(stack.size() - 1).getUserObject(); } @Override @@ -229,7 +248,7 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac @Override public void pushController(String displayName, String iconLeftCss, Controller controller) { - TooledBreadCrumb currentCrumb = getCurrentCrumb(); + BreadCrumb currentCrumb = getCurrentCrumb(); if(currentCrumb == null || currentCrumb.getController() != controller) { super.pushController(displayName, iconLeftCss, controller); if(controller instanceof TooledController) { @@ -279,6 +298,28 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac public void setBreadcrumbEnabled(boolean breadcrumbEnabled) { this.breadcrumbEnabled = breadcrumbEnabled; } + + public void setToolsLimit(Align slot, int maxNumberOfTools, String dropdownI18nKey) { + setToolsLimit(slot, maxNumberOfTools, dropdownI18nKey, null); + } + + /** + * + * @param slot The slot to limit + * @param maxNumberOfTools The maximum of tools visible in the toolbar + * @param dropdownI18nKey The i18n key to label the dropdown + * @param dropdownIconCss The CSS class to decorate the dropdown + */ + public void setToolsLimit(Align slot, int maxNumberOfTools, String dropdownI18nKey, String dropdownIconCss) { + ToolsSlot config = toolsSlots.get(slot); + config.setLimitOfTools(maxNumberOfTools); + config.setToolDropdownI18nKey(dropdownI18nKey); + if(StringHelper.containsNonWhitespace(dropdownIconCss)) { + config.setToolDropdownIconCss(dropdownIconCss); + } else { + config.setToolDropdownIconCss("o_icon o_icon_menuhandel"); + } + } public String getMessage() { return message; @@ -306,63 +347,113 @@ public class TooledStackedPanel extends BreadcrumbedStackedPanel implements Stac setDirty(true); } } - - public static class Tool { - private final Align align; - private final boolean inherit; - private final Component component; - private String toolCss; + + public enum Align { + left("o_tools_left"), + center("o_tools_center"), + right("o_tools_right"), + rightEdge("o_tools_right_edge"), + segment("o_tools_segments"); + + private final String cssClass; - public Tool(Component component, Align align, boolean inherit, String toolCss) { - this.align = align; - this.inherit = inherit; - this.component = component; - this.toolCss = toolCss; + private Align(String cssClass) { + this.cssClass = cssClass; } - public boolean isInherit() { - return inherit; + public String cssClass() { + return cssClass; + } + } + + public class ToolsSlot { + + private final Align slot; + private int limitOfTools = 32; + private String toolDropdownI18nKey; + private String toolDropdownIconCss; + + public ToolsSlot(Align slot) { + this.slot = slot; } - public Align getAlign() { - return align; + public int getLimitOfTools() { + return limitOfTools; } - public Component getComponent() { - return component; + public void setLimitOfTools(int limitOfTools) { + this.limitOfTools = limitOfTools; } - - public String getToolCss() { - return toolCss; + + public Align getSlot() { + return slot; + } + + public String getToolDropdownI18nKey() { + return toolDropdownI18nKey; + } + + public void setToolDropdownI18nKey(String i18nKey) { + toolDropdownI18nKey = i18nKey; + } + + public String getToolDropdownIconCss() { + return toolDropdownIconCss; + } + + public void setToolDropdownIconCss(String toolDropdownIconCss) { + this.toolDropdownIconCss = toolDropdownIconCss; } - } - public static class TooledBreadCrumb extends BreadCrumb { - private final List<Tool> tools = new ArrayList<>(5); - - public TooledBreadCrumb(Controller controller, Object uobject) { - super(controller, uobject); + public class ToolBar extends AbstractComponent implements ComponentCollection { + + public ToolBar(String id) { + super(id, null, null); + setDomReplacementWrapperRequired(false); } - public List<Tool> getTools() { - return tools; + public TooledStackedPanel getPanel() { + return TooledStackedPanel.this; } - public void addTool(Tool tool) { - tools.add(tool); + public ToolsSlot getSlot(Align align) { + return toolsSlots.get(align); } - public void removeTool(Tool tool) { - tools.remove(tool); + @Override + public Translator getTranslator() { + return TooledStackedPanel.this.getTranslator(); + } + + @Override + public Component getComponent(String name) { + for(Tool tool:getTools()) { + Component cmp = tool.getComponent(); + if(cmp != null && cmp.getComponentName().equals(name)) { + return cmp; + } + } + return null; + } + + @Override + public Iterable<Component> getComponents() { + List<Component> cmps = new ArrayList<>(); + for(Tool tool:getTools()) { + cmps.add(tool.getComponent()); + } + return cmps; + } + + @Override + protected void doDispatchRequest(UserRequest ureq) { + // + } + + @Override + public ComponentRenderer getHTMLRendererSingleton() { + return TOOLS_RENDERER; } } - - public enum Align { - left, - right, - rightEdge, - segment - } - } \ No newline at end of file diff --git a/src/main/java/org/olat/core/gui/components/stack/TooledStackedPanelRenderer.java b/src/main/java/org/olat/core/gui/components/stack/TooledStackedPanelRenderer.java index f359b3b5097..79d5d0bcbcb 100644 --- a/src/main/java/org/olat/core/gui/components/stack/TooledStackedPanelRenderer.java +++ b/src/main/java/org/olat/core/gui/components/stack/TooledStackedPanelRenderer.java @@ -19,16 +19,13 @@ */ package org.olat.core.gui.components.stack; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.DefaultComponentRenderer; -import org.olat.core.gui.components.dropdown.Dropdown; import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.stack.BreadcrumbedStackedPanel.Tool; import org.olat.core.gui.components.stack.TooledStackedPanel.Align; -import org.olat.core.gui.components.stack.TooledStackedPanel.Tool; import org.olat.core.gui.render.RenderResult; import org.olat.core.gui.render.Renderer; import org.olat.core.gui.render.StringOutput; @@ -57,86 +54,17 @@ public class TooledStackedPanelRenderer extends DefaultComponentRenderer { if((panel.isBreadcrumbEnabled() && breadCrumbs.size() > panel.getInvisibleCrumb()) || (!tools.isEmpty() && panel.isToolbarEnabled())) { sb.append("<div id='o_main_toolbar' class='o_toolbar"); - if ((panel.isToolbarAutoEnabled() || panel.isToolbarEnabled() ) && !getTools(tools, Align.segment).isEmpty()) { + if ((panel.isToolbarAutoEnabled() || panel.isToolbarEnabled() ) && !panel.getTools(Align.segment).isEmpty()) { sb.append(" o_toolbar_with_segments"); } sb.append("'>"); - if(panel.isBreadcrumbEnabled() && breadCrumbs.size() > panel.getInvisibleCrumb()) { - sb.append("<div class='o_breadcrumb'><ol class='breadcrumb'>"); - Link backLink = panel.getBackLink(); - int numOfCrumbs = breadCrumbs.size(); - if(backLink.isVisible() && numOfCrumbs > panel.getInvisibleCrumb()) { - sb.append("<li class='o_breadcrumb_back'>"); - backLink.getHTMLRendererSingleton().render(renderer, sb, backLink, ubu, translator, renderResult, args); - sb.append("</li>"); - - for(Link crumb:breadCrumbs) { - sb.append("<li").append(" class='active'", breadCrumbs.indexOf(crumb) == numOfCrumbs-1).append(">"); - renderer.render(crumb, sb, args); - sb.append("</li>"); - } - } - - Link closeLink = panel.getCloseLink(); - if (closeLink.isVisible()) { - sb.append("<li class='o_breadcrumb_close'>"); - closeLink.getHTMLRendererSingleton().render(renderer, sb, closeLink, ubu, translator, renderResult, args); - sb.append("</li>"); - } - - sb.append("</ol></div>"); // o_breadcrumb + if(panel.isBreadcrumbEnabled() && breadCrumbs.size() > panel.getInvisibleCrumb()) { + renderer.render(panel.getBreadcrumbBar(), sb, args); } if (panel.isToolbarAutoEnabled() || panel.isToolbarEnabled()) { - List<Tool> leftTools = getTools(tools, Align.left); - List<Tool> rightEdgeTools = getTools(tools, Align.rightEdge); - List<Tool> rightTools = getTools(tools, Align.right); - List<Tool> segmentsTools = getTools(tools, Align.segment); - List<Tool> notAlignedTools = getTools(tools, null); - - if(panel.isToolbarEnabled() || (panel.isToolbarAutoEnabled() - && (!leftTools.isEmpty() || !rightTools.isEmpty() || !notAlignedTools.isEmpty() || !segmentsTools.isEmpty()))) { - sb.append("<div class='o_tools_container'><div class='container-fluid'>"); - - if(!leftTools.isEmpty()) { - sb.append("<ul class='o_tools o_tools_left list-inline'>"); - renderTools(leftTools, renderer, sb, args); - sb.append("</ul>"); - } - - if(!rightEdgeTools.isEmpty()) { - sb.append("<ul class='o_tools o_tools_right_edge list-inline'>"); - renderTools(rightEdgeTools, renderer, sb, args); - sb.append("</ul>"); - } - - if(!rightTools.isEmpty()) { - sb.append("<ul class='o_tools o_tools_right list-inline'>"); - renderTools(rightTools, renderer, sb, args); - sb.append("</ul>"); - } - - if(!notAlignedTools.isEmpty()) { - sb.append("<ul class='o_tools o_tools_center list-inline'>"); - renderTools(notAlignedTools, renderer, sb, args); - sb.append("</ul>"); - } - sb.append("</div>"); // container-fluid, - - if(!segmentsTools.isEmpty()) { - boolean segmentAlone = leftTools.isEmpty() && rightTools.isEmpty() && notAlignedTools.isEmpty(); - sb.append("<ul class='o_tools o_tools_segments list-inline") - .append(" o_tools_segments_alone", segmentAlone).append("'>"); - - Tool segmentTool = segmentsTools.get(segmentsTools.size() - 1); - List<Tool> lastSegmentTool = Collections.singletonList(segmentTool); - renderTools(lastSegmentTool, renderer, sb, args); - - sb.append("</ul>"); - } - sb.append("</div>"); - } + renderer.render(panel.getToolBar(), sb, args); } sb.append("</div>"); // o_toolbar } @@ -152,6 +80,7 @@ public class TooledStackedPanelRenderer extends DefaultComponentRenderer { Component messageCmp = panel.getMessageComponent(); URLBuilder cubu = ubu.createCopyFor(messageCmp); messageCmp.getHTMLRendererSingleton().render(renderer, sb, messageCmp, cubu, translator, renderResult, args); + messageCmp.setDirty(false); } Component toRender = panel.getContent(); @@ -161,44 +90,4 @@ public class TooledStackedPanelRenderer extends DefaultComponentRenderer { sb.append("</div>"); // end of panel div } - - private List<Tool> getTools(List<Tool> tools, Align alignement) { - List<Tool> alignedTools = new ArrayList<>(tools.size()); - if(alignement == null) { - for(Tool tool:tools) { - if(tool.getAlign() == null && tool.getComponent().isVisible()) { - alignedTools.add(tool); - } - } - } else { - for(Tool tool:tools) { - if(alignement.equals(tool.getAlign()) && tool.getComponent().isVisible()) { - alignedTools.add(tool); - } - } - } - return alignedTools; - } - - private void renderTools(List<Tool> tools, Renderer renderer, StringOutput sb, String[] args) { - int numOfTools = tools.size(); - for(int i=0; i<numOfTools; i++) { - Tool tool = tools.get(i); - Component cmp = tool.getComponent(); - String cssClass = tool.getToolCss(); - if (cssClass == null) { - // use defaults - if(cmp instanceof Dropdown) { - cssClass = "o_tool_dropdown dropdown"; - } else if(cmp instanceof Link && !cmp.isEnabled()) { - cssClass = "o_text"; - } else { - cssClass = "o_tool"; - } - } - sb.append("<li class='").append(cssClass).append("'>"); - renderer.render(cmp, sb, args); - sb.append("</li>"); - } - } } \ No newline at end of file diff --git a/src/main/java/org/olat/core/gui/render/Renderer.java b/src/main/java/org/olat/core/gui/render/Renderer.java index 96db777b724..e4ac409e2e0 100644 --- a/src/main/java/org/olat/core/gui/render/Renderer.java +++ b/src/main/java/org/olat/core/gui/render/Renderer.java @@ -34,7 +34,6 @@ import org.olat.core.gui.components.ComponentRenderer; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.render.intercept.InterceptHandlerInstance; import org.olat.core.gui.translator.Translator; -import org.olat.core.helpers.Settings; import org.olat.core.logging.AssertException; import org.olat.core.util.WebappHelper; @@ -79,13 +78,13 @@ public class Renderer { * use renderStaticURI * * @param target - * @param URI e.g. myspecialdispatcher/somestuff + * @param uri e.g. myspecialdispatcher/somestuff */ - public static void renderNormalURI(StringOutput target, String URI) { + public static void renderNormalURI(StringOutput target, String uri) { String root = WebappHelper.getServletContextPath(); target.append(root); // e.g /olat target.append("/"); - target.append(URI); + target.append(uri); } /** @@ -96,11 +95,11 @@ public class Renderer { * renders a uri which is mounted to the webapp/static/ directory of your webapplication. * * @param target - * @param URI e.g. img/specialimagenotpossiblewithcss.jpg + * @param uri e.g. img/specialimagenotpossiblewithcss.jpg */ - public static void renderStaticURI(StringOutput target, String URI) { + public static void renderStaticURI(StringOutput target, String uri) { // forward to static dispatcher that knows how to deliver the static files! - StaticMediaDispatcher.renderStaticURI(target, URI); + StaticMediaDispatcher.renderStaticURI(target, uri); } /** @@ -172,8 +171,7 @@ public class Renderer { * @return */ public Component findComponent(String componentName) { - Component source = renderContainer.getComponent(componentName); - return source; + return renderContainer.getComponent(componentName); } /** @@ -214,9 +212,6 @@ public class Renderer { } else { sb.append("<div id='o_c").append(source.getDispatchID()); } - if(Settings.isDebuging()) { - //sb.append("' title='").append(source.getComponentName()); - } sb.append("'>"); } -- GitLab